mirror of https://github.com/python/cpython
gh-111495: Add tests for `PyCodec_*` C API (#123343)
This commit is contained in:
parent
4d8e7c40a0
commit
6d0d26eb8c
|
@ -1,13 +1,20 @@
|
||||||
import unittest
|
import codecs
|
||||||
|
import contextlib
|
||||||
|
import io
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
import unittest.mock as mock
|
||||||
|
import _testcapi
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
|
|
||||||
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
|
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
|
||||||
|
|
||||||
NULL = None
|
NULL = None
|
||||||
|
BAD_ARGUMENT = re.escape('bad argument type for built-in operation')
|
||||||
|
|
||||||
|
|
||||||
class CAPITest(unittest.TestCase):
|
class CAPIUnicodeTest(unittest.TestCase):
|
||||||
# TODO: Test the following functions:
|
# TODO: Test the following functions:
|
||||||
#
|
#
|
||||||
# PyUnicode_BuildEncodingMap
|
# PyUnicode_BuildEncodingMap
|
||||||
|
@ -516,5 +523,291 @@ class CAPITest(unittest.TestCase):
|
||||||
# CRASHES asrawunicodeescapestring(NULL)
|
# CRASHES asrawunicodeescapestring(NULL)
|
||||||
|
|
||||||
|
|
||||||
|
class CAPICodecs(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Encoding names are normalized internally by converting them
|
||||||
|
# to lowercase and their hyphens are replaced by underscores.
|
||||||
|
self.encoding_name = 'test.test_capi.test_codecs.codec_reversed'
|
||||||
|
# Make sure that our custom codec is not already registered (that
|
||||||
|
# way we know whether we correctly unregistered the custom codec
|
||||||
|
# after a test or not).
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, self.encoding_name)
|
||||||
|
# create the search function without registering yet
|
||||||
|
self._create_custom_codec()
|
||||||
|
|
||||||
|
def _create_custom_codec(self):
|
||||||
|
def codec_encoder(m, errors='strict'):
|
||||||
|
return (type(m)().join(reversed(m)), len(m))
|
||||||
|
|
||||||
|
def codec_decoder(c, errors='strict'):
|
||||||
|
return (type(c)().join(reversed(c)), len(c))
|
||||||
|
|
||||||
|
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||||
|
def encode(self, input, final=False):
|
||||||
|
return codec_encoder(input)
|
||||||
|
|
||||||
|
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||||
|
def decode(self, input, final=False):
|
||||||
|
return codec_decoder(input)
|
||||||
|
|
||||||
|
class StreamReader(codecs.StreamReader):
|
||||||
|
def encode(self, input, errors='strict'):
|
||||||
|
return codec_encoder(input, errors=errors)
|
||||||
|
|
||||||
|
def decode(self, input, errors='strict'):
|
||||||
|
return codec_decoder(input, errors=errors)
|
||||||
|
|
||||||
|
class StreamWriter(codecs.StreamWriter):
|
||||||
|
def encode(self, input, errors='strict'):
|
||||||
|
return codec_encoder(input, errors=errors)
|
||||||
|
|
||||||
|
def decode(self, input, errors='strict'):
|
||||||
|
return codec_decoder(input, errors=errors)
|
||||||
|
|
||||||
|
info = codecs.CodecInfo(
|
||||||
|
encode=codec_encoder,
|
||||||
|
decode=codec_decoder,
|
||||||
|
streamreader=StreamReader,
|
||||||
|
streamwriter=StreamWriter,
|
||||||
|
incrementalencoder=IncrementalEncoder,
|
||||||
|
incrementaldecoder=IncrementalDecoder,
|
||||||
|
name=self.encoding_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def search_function(encoding):
|
||||||
|
if encoding == self.encoding_name:
|
||||||
|
return info
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.codec_info = info
|
||||||
|
self.search_function = search_function
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def use_custom_encoder(self):
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, self.encoding_name)
|
||||||
|
codecs.register(self.search_function)
|
||||||
|
yield
|
||||||
|
codecs.unregister(self.search_function)
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, self.encoding_name)
|
||||||
|
|
||||||
|
def test_codec_register(self):
|
||||||
|
search_function, encoding = self.search_function, self.encoding_name
|
||||||
|
# register the search function using the C API
|
||||||
|
self.assertIsNone(_testcapi.codec_register(search_function))
|
||||||
|
# in case the test failed before cleaning up
|
||||||
|
self.addCleanup(codecs.unregister, self.search_function)
|
||||||
|
self.assertIs(codecs.lookup(encoding), search_function(encoding))
|
||||||
|
self.assertEqual(codecs.encode('123', encoding=encoding), '321')
|
||||||
|
# unregister the search function using the regular API
|
||||||
|
codecs.unregister(search_function)
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, encoding)
|
||||||
|
|
||||||
|
def test_codec_unregister(self):
|
||||||
|
search_function, encoding = self.search_function, self.encoding_name
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, encoding)
|
||||||
|
# register the search function using the regular API
|
||||||
|
codecs.register(search_function)
|
||||||
|
# in case the test failed before cleaning up
|
||||||
|
self.addCleanup(codecs.unregister, self.search_function)
|
||||||
|
self.assertIsNotNone(codecs.lookup(encoding))
|
||||||
|
# unregister the search function using the C API
|
||||||
|
self.assertIsNone(_testcapi.codec_unregister(search_function))
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, encoding)
|
||||||
|
|
||||||
|
def test_codec_known_encoding(self):
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, 'unknown-codec')
|
||||||
|
self.assertFalse(_testcapi.codec_known_encoding('unknown-codec'))
|
||||||
|
self.assertFalse(_testcapi.codec_known_encoding('unknown_codec'))
|
||||||
|
self.assertFalse(_testcapi.codec_known_encoding('UNKNOWN-codec'))
|
||||||
|
|
||||||
|
encoding_name = self.encoding_name
|
||||||
|
self.assertRaises(LookupError, codecs.lookup, encoding_name)
|
||||||
|
|
||||||
|
codecs.register(self.search_function)
|
||||||
|
self.addCleanup(codecs.unregister, self.search_function)
|
||||||
|
|
||||||
|
for name in [
|
||||||
|
encoding_name,
|
||||||
|
encoding_name.upper(),
|
||||||
|
encoding_name.replace('_', '-'),
|
||||||
|
]:
|
||||||
|
with self.subTest(name):
|
||||||
|
self.assertTrue(_testcapi.codec_known_encoding(name))
|
||||||
|
|
||||||
|
def test_codec_encode(self):
|
||||||
|
encode = _testcapi.codec_encode
|
||||||
|
self.assertEqual(encode('a', 'utf-8', NULL), b'a')
|
||||||
|
self.assertEqual(encode('a', 'utf-8', 'strict'), b'a')
|
||||||
|
self.assertEqual(encode('[é]', 'ascii', 'ignore'), b'[]')
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, encode, NULL, 'ascii', 'strict')
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
encode('a', NULL, 'strict')
|
||||||
|
|
||||||
|
def test_codec_decode(self):
|
||||||
|
decode = _testcapi.codec_decode
|
||||||
|
|
||||||
|
s = 'a\xa1\u4f60\U0001f600'
|
||||||
|
b = s.encode()
|
||||||
|
|
||||||
|
self.assertEqual(decode(b, 'utf-8', 'strict'), s)
|
||||||
|
self.assertEqual(decode(b, 'utf-8', NULL), s)
|
||||||
|
self.assertEqual(decode(b, 'latin1', 'strict'), b.decode('latin1'))
|
||||||
|
self.assertRaises(UnicodeDecodeError, decode, b, 'ascii', 'strict')
|
||||||
|
self.assertRaises(UnicodeDecodeError, decode, b, 'ascii', NULL)
|
||||||
|
self.assertEqual(decode(b, 'ascii', 'replace'), 'a' + '\ufffd'*9)
|
||||||
|
|
||||||
|
# _codecs.decode() only reports an unknown error handling name when
|
||||||
|
# the corresponding error handling function is used; this difers
|
||||||
|
# from PyUnicode_Decode() which checks that both the encoding and
|
||||||
|
# the error handling name are recognized before even attempting to
|
||||||
|
# call the decoder.
|
||||||
|
self.assertEqual(decode(b'', 'utf-8', 'unknown-error-handler'), '')
|
||||||
|
self.assertEqual(decode(b'a', 'utf-8', 'unknown-error-handler'), 'a')
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, decode, NULL, 'ascii', 'strict')
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
decode(b, NULL, 'strict')
|
||||||
|
|
||||||
|
def test_codec_encoder(self):
|
||||||
|
codec_encoder = _testcapi.codec_encoder
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
encoder = codec_encoder(self.encoding_name)
|
||||||
|
self.assertIs(encoder, self.codec_info.encode)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_encoder(NULL)
|
||||||
|
|
||||||
|
def test_codec_decoder(self):
|
||||||
|
codec_decoder = _testcapi.codec_decoder
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
decoder = codec_decoder(self.encoding_name)
|
||||||
|
self.assertIs(decoder, self.codec_info.decode)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_decoder(NULL)
|
||||||
|
|
||||||
|
def test_codec_incremental_encoder(self):
|
||||||
|
codec_incremental_encoder = _testcapi.codec_incremental_encoder
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
encoding = self.encoding_name
|
||||||
|
|
||||||
|
for errors in ['strict', NULL]:
|
||||||
|
with self.subTest(errors):
|
||||||
|
encoder = codec_incremental_encoder(encoding, errors)
|
||||||
|
self.assertIsInstance(encoder, self.codec_info.incrementalencoder)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_incremental_encoder(NULL, 'strict')
|
||||||
|
|
||||||
|
def test_codec_incremental_decoder(self):
|
||||||
|
codec_incremental_decoder = _testcapi.codec_incremental_decoder
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
encoding = self.encoding_name
|
||||||
|
|
||||||
|
for errors in ['strict', NULL]:
|
||||||
|
with self.subTest(errors):
|
||||||
|
decoder = codec_incremental_decoder(encoding, errors)
|
||||||
|
self.assertIsInstance(decoder, self.codec_info.incrementaldecoder)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_incremental_decoder(NULL, 'strict')
|
||||||
|
|
||||||
|
def test_codec_stream_reader(self):
|
||||||
|
codec_stream_reader = _testcapi.codec_stream_reader
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
encoding, stream = self.encoding_name, io.StringIO()
|
||||||
|
for errors in ['strict', NULL]:
|
||||||
|
with self.subTest(errors):
|
||||||
|
writer = codec_stream_reader(encoding, stream, errors)
|
||||||
|
self.assertIsInstance(writer, self.codec_info.streamreader)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_stream_reader(NULL, stream, 'strict')
|
||||||
|
|
||||||
|
def test_codec_stream_writer(self):
|
||||||
|
codec_stream_writer = _testcapi.codec_stream_writer
|
||||||
|
|
||||||
|
with self.use_custom_encoder():
|
||||||
|
encoding, stream = self.encoding_name, io.StringIO()
|
||||||
|
for errors in ['strict', NULL]:
|
||||||
|
with self.subTest(errors):
|
||||||
|
writer = codec_stream_writer(encoding, stream, errors)
|
||||||
|
self.assertIsInstance(writer, self.codec_info.streamwriter)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, BAD_ARGUMENT):
|
||||||
|
codec_stream_writer(NULL, stream, 'strict')
|
||||||
|
|
||||||
|
|
||||||
|
class CAPICodecErrors(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_codec_register_error(self):
|
||||||
|
# for cleaning up between tests
|
||||||
|
from _codecs import _unregister_error as _codecs_unregister_error
|
||||||
|
|
||||||
|
self.assertRaises(LookupError, _testcapi.codec_lookup_error, 'custom')
|
||||||
|
|
||||||
|
def custom_error_handler(exc):
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
error_handler = mock.Mock(wraps=custom_error_handler)
|
||||||
|
_testcapi.codec_register_error('custom', error_handler)
|
||||||
|
self.addCleanup(_codecs_unregister_error, 'custom')
|
||||||
|
|
||||||
|
self.assertRaises(UnicodeEncodeError, codecs.encode,
|
||||||
|
'\xff', 'ascii', errors='custom')
|
||||||
|
error_handler.assert_called_once()
|
||||||
|
error_handler.reset_mock()
|
||||||
|
|
||||||
|
self.assertRaises(UnicodeDecodeError, codecs.decode,
|
||||||
|
b'\xff', 'ascii', errors='custom')
|
||||||
|
error_handler.assert_called_once()
|
||||||
|
|
||||||
|
# _codecs._unregister_error directly delegates to the internal C
|
||||||
|
# function so a Python-level function test is sufficient (it is
|
||||||
|
# tested in test_codeccallbacks).
|
||||||
|
|
||||||
|
def test_codec_lookup_error(self):
|
||||||
|
codec_lookup_error = _testcapi.codec_lookup_error
|
||||||
|
self.assertIs(codec_lookup_error(NULL), codecs.strict_errors)
|
||||||
|
self.assertIs(codec_lookup_error('strict'), codecs.strict_errors)
|
||||||
|
self.assertIs(codec_lookup_error('ignore'), codecs.ignore_errors)
|
||||||
|
self.assertIs(codec_lookup_error('replace'), codecs.replace_errors)
|
||||||
|
self.assertIs(codec_lookup_error('xmlcharrefreplace'), codecs.xmlcharrefreplace_errors)
|
||||||
|
self.assertIs(codec_lookup_error('namereplace'), codecs.namereplace_errors)
|
||||||
|
self.assertRaises(LookupError, codec_lookup_error, 'unknown')
|
||||||
|
|
||||||
|
def test_codec_error_handlers(self):
|
||||||
|
exceptions = [
|
||||||
|
# A UnicodeError with an empty message currently crashes:
|
||||||
|
# See: https://github.com/python/cpython/issues/123378
|
||||||
|
# UnicodeEncodeError('bad', '', 0, 1, 'reason'),
|
||||||
|
UnicodeEncodeError('bad', 'x', 0, 1, 'reason'),
|
||||||
|
UnicodeEncodeError('bad', 'xyz123', 0, 1, 'reason'),
|
||||||
|
UnicodeEncodeError('bad', 'xyz123', 1, 4, 'reason'),
|
||||||
|
]
|
||||||
|
|
||||||
|
strict_handler = _testcapi.codec_strict_errors
|
||||||
|
for exc in exceptions:
|
||||||
|
with self.subTest(handler=strict_handler, exc=exc):
|
||||||
|
self.assertRaises(UnicodeEncodeError, strict_handler, exc)
|
||||||
|
|
||||||
|
for handler in [
|
||||||
|
_testcapi.codec_ignore_errors,
|
||||||
|
_testcapi.codec_replace_errors,
|
||||||
|
_testcapi.codec_xmlcharrefreplace_errors,
|
||||||
|
_testlimitedcapi.codec_namereplace_errors,
|
||||||
|
]:
|
||||||
|
for exc in exceptions:
|
||||||
|
with self.subTest(handler=handler, exc=exc):
|
||||||
|
self.assertIsInstance(handler(exc), tuple)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -163,7 +163,7 @@
|
||||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
|
||||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c
|
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c
|
||||||
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
|
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
|
||||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||||
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,213 @@
|
||||||
#include "parts.h"
|
#include "parts.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
// === Codecs registration and un-registration ================================
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_register(PyObject *Py_UNUSED(module), PyObject *search_function)
|
||||||
|
{
|
||||||
|
if (PyCodec_Register(search_function) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_unregister(PyObject *Py_UNUSED(module), PyObject *search_function)
|
||||||
|
{
|
||||||
|
if (PyCodec_Unregister(search_function) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_known_encoding(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
if (!PyArg_ParseTuple(args, "z", &encoding)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_KnownEncoding(encoding) ? Py_True : Py_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Codecs encoding and decoding interfaces ================================
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_encode(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *input;
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "O|zz", &input, &encoding, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_Encode(input, encoding, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_decode(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *input;
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "O|zz", &input, &encoding, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_Decode(input, encoding, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_encoder(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
if (!PyArg_ParseTuple(args, "z", &encoding)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_Encoder(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_decoder(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
if (!PyArg_ParseTuple(args, "z", &encoding)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_Decoder(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_incremental_encoder(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "zz", &encoding, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_IncrementalEncoder(encoding, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_incremental_decoder(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "zz", &encoding, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_IncrementalDecoder(encoding, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_stream_reader(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
PyObject *stream;
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "zOz", &encoding, &stream, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_StreamReader(encoding, stream, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_stream_writer(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
PyObject *stream;
|
||||||
|
const char *errors; // can be NULL
|
||||||
|
if (!PyArg_ParseTuple(args, "zOz", &encoding, &stream, &errors)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_StreamWriter(encoding, stream, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Codecs errors handlers =================================================
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_register_error(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *encoding; // must not be NULL
|
||||||
|
PyObject *error;
|
||||||
|
if (!PyArg_ParseTuple(args, "sO", &encoding, &error)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PyCodec_RegisterError(encoding, error) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_lookup_error(PyObject *Py_UNUSED(module), PyObject *args)
|
||||||
|
{
|
||||||
|
const char *NULL_WOULD_RAISE(encoding); // NULL case will be tested
|
||||||
|
if (!PyArg_ParseTuple(args, "z", &encoding)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyCodec_LookupError(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_strict_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_StrictErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_ignore_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_IgnoreErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_replace_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_ReplaceErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_xmlcharrefreplace_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_XMLCharRefReplaceErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_backslashreplace_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_BackslashReplaceErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{NULL},
|
/* codecs registration */
|
||||||
|
{"codec_register", codec_register, METH_O},
|
||||||
|
{"codec_unregister", codec_unregister, METH_O},
|
||||||
|
{"codec_known_encoding", codec_known_encoding, METH_VARARGS},
|
||||||
|
/* encoding and decoding interface */
|
||||||
|
{"codec_encode", codec_encode, METH_VARARGS},
|
||||||
|
{"codec_decode", codec_decode, METH_VARARGS},
|
||||||
|
{"codec_encoder", codec_encoder, METH_VARARGS},
|
||||||
|
{"codec_decoder", codec_decoder, METH_VARARGS},
|
||||||
|
{"codec_incremental_encoder", codec_incremental_encoder, METH_VARARGS},
|
||||||
|
{"codec_incremental_decoder", codec_incremental_decoder, METH_VARARGS},
|
||||||
|
{"codec_stream_reader", codec_stream_reader, METH_VARARGS},
|
||||||
|
{"codec_stream_writer", codec_stream_writer, METH_VARARGS},
|
||||||
|
/* error handling */
|
||||||
|
{"codec_register_error", codec_register_error, METH_VARARGS},
|
||||||
|
{"codec_lookup_error", codec_lookup_error, METH_VARARGS},
|
||||||
|
{"codec_strict_errors", codec_strict_errors, METH_O},
|
||||||
|
{"codec_ignore_errors", codec_ignore_errors, METH_O},
|
||||||
|
{"codec_replace_errors", codec_replace_errors, METH_O},
|
||||||
|
{"codec_xmlcharrefreplace_errors", codec_xmlcharrefreplace_errors, METH_O},
|
||||||
|
{"codec_backslashreplace_errors", codec_backslashreplace_errors, METH_O},
|
||||||
|
// PyCodec_NameReplaceErrors() is tested in _testlimitedcapi/codec.c
|
||||||
|
{NULL, NULL, 0, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -31,3 +31,13 @@ static const char uninitialized[] = "uninitialized";
|
||||||
#define UNINITIALIZED_SIZE ((Py_ssize_t)236892191)
|
#define UNINITIALIZED_SIZE ((Py_ssize_t)236892191)
|
||||||
/* Marker to check that integer value was set. */
|
/* Marker to check that integer value was set. */
|
||||||
#define UNINITIALIZED_INT (63256717)
|
#define UNINITIALIZED_INT (63256717)
|
||||||
|
/*
|
||||||
|
* Marker to indicate that a NULL parameter would not be allowed
|
||||||
|
* at runtime but that the test interface will check that it is
|
||||||
|
* indeed the case.
|
||||||
|
*
|
||||||
|
* Use this macro only if passing NULL to the C API would raise
|
||||||
|
* a catchable exception (and not a fatal exception that would
|
||||||
|
* crash the interpreter).
|
||||||
|
*/
|
||||||
|
#define NULL_WOULD_RAISE(NAME) NAME
|
||||||
|
|
|
@ -38,6 +38,9 @@ PyInit__testlimitedcapi(void)
|
||||||
if (_PyTestLimitedCAPI_Init_Bytes(mod) < 0) {
|
if (_PyTestLimitedCAPI_Init_Bytes(mod) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (_PyTestLimitedCAPI_Init_Codec(mod) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (_PyTestLimitedCAPI_Init_Complex(mod) < 0) {
|
if (_PyTestLimitedCAPI_Init_Complex(mod) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "pyconfig.h" // Py_GIL_DISABLED
|
||||||
|
|
||||||
|
// Need limited C API version 3.5 for PyCodec_NameReplaceErrors()
|
||||||
|
#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
|
||||||
|
# define Py_LIMITED_API 0x03050000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "parts.h"
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
codec_namereplace_errors(PyObject *Py_UNUSED(module), PyObject *exc)
|
||||||
|
{
|
||||||
|
assert(exc != NULL);
|
||||||
|
return PyCodec_NameReplaceErrors(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef test_methods[] = {
|
||||||
|
{"codec_namereplace_errors", codec_namereplace_errors, METH_O},
|
||||||
|
{NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyTestLimitedCAPI_Init_Codec(PyObject *module)
|
||||||
|
{
|
||||||
|
if (PyModule_AddFunctions(module, test_methods) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -25,6 +25,7 @@
|
||||||
int _PyTestLimitedCAPI_Init_Abstract(PyObject *module);
|
int _PyTestLimitedCAPI_Init_Abstract(PyObject *module);
|
||||||
int _PyTestLimitedCAPI_Init_ByteArray(PyObject *module);
|
int _PyTestLimitedCAPI_Init_ByteArray(PyObject *module);
|
||||||
int _PyTestLimitedCAPI_Init_Bytes(PyObject *module);
|
int _PyTestLimitedCAPI_Init_Bytes(PyObject *module);
|
||||||
|
int _PyTestLimitedCAPI_Init_Codec(PyObject *module);
|
||||||
int _PyTestLimitedCAPI_Init_Complex(PyObject *module);
|
int _PyTestLimitedCAPI_Init_Complex(PyObject *module);
|
||||||
int _PyTestLimitedCAPI_Init_Dict(PyObject *module);
|
int _PyTestLimitedCAPI_Init_Dict(PyObject *module);
|
||||||
int _PyTestLimitedCAPI_Init_Eval(PyObject *module);
|
int _PyTestLimitedCAPI_Init_Eval(PyObject *module);
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\abstract.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\abstract.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\bytearray.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\bytearray.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\bytes.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\bytes.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testlimitedcapi\codec.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\complex.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\complex.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\dict.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\dict.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\eval.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\eval.c" />
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\abstract.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\abstract.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\bytearray.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\bytearray.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\bytes.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\bytes.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testlimitedcapi\codec.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\complex.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\complex.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\dict.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\dict.c" />
|
||||||
<ClCompile Include="..\Modules\_testlimitedcapi\eval.c" />
|
<ClCompile Include="..\Modules\_testlimitedcapi\eval.c" />
|
||||||
|
|
Loading…
Reference in New Issue