Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions Doc/c-api/conversion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,33 @@ The following functions provide locale-independent string to number conversions.
.. versionadded:: 3.1


.. c:function:: int PyOS_stricmp(const char *s1, const char *s2)
.. c:function:: int PyOS_mystricmp(const char *str1, const char *str2)
int PyOS_mystrnicmp(const char *str1, const char *str2, Py_ssize_t size)

Case insensitive comparison of strings. The function works almost
identically to :c:func:`!strcmp` except that it ignores the case.
Case insensitive comparison of strings. These functions work almost
identically to :c:func:`!strcmp` and :c:func:`!strncmp` (respectively),
except that they ignore the case of ASCII characters.

Return ``0`` if the strings are equal, a negative value if *str1* sorts
lexicographically before *str2*, or a positive value if it sorts after.

.. c:function:: int PyOS_strnicmp(const char *s1, const char *s2, Py_ssize_t size)
In the *str1* or *str2* arguments, a NUL byte marks the end of the string.
For :c:func:`!PyOS_mystrnicmp`, the *size* argument gives the maximum size
of the string, as if NUL was present at the index given by *size*.

Case insensitive comparison of strings. The function works almost
identically to :c:func:`!strncmp` except that it ignores the case.
These functions do not use the locale.


.. c:function:: int PyOS_stricmp(const char *str1, const char *str2)
int PyOS_strnicmp(const char *str1, const char *str2, Py_ssize_t size)

Case insensitive comparison of strings.

On Windows, these are aliases of :c:func:`!stricmp` and :c:func:`!strnicmp`,
respectively.

On other platforms, they are aliases of :c:func:`PyOS_mystricmp` and
:c:func:`PyOS_mystrnicmp`, respectively.


Character classification and conversion
Expand Down
4 changes: 4 additions & 0 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ The available slot types are:
``PyModuleDef`` has non-``NULL`` ``m_traverse``, ``m_clear``,
``m_free``; non-zero ``m_size``; or slots other than ``Py_mod_create``.

.. versionadded:: 3.5

.. c:macro:: Py_mod_exec

Specifies a function that is called to *execute* the module.
Expand All @@ -342,6 +344,8 @@ The available slot types are:
If multiple ``Py_mod_exec`` slots are specified, they are processed in the
order they appear in the *m_slots* array.

.. versionadded:: 3.5

.. c:macro:: Py_mod_multiple_interpreters

Specifies one of the following values:
Expand Down
8 changes: 8 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 55 additions & 41 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ struct _object {
# endif
};
#else
Py_ssize_t ob_refcnt;
Py_ssize_t ob_refcnt; // part of stable ABI; do not change
#endif
_Py_ALIGNED_DEF(_PyObject_MIN_ALIGNMENT, char) _aligner;
};

PyTypeObject *ob_type;
PyTypeObject *ob_type; // part of stable ABI; do not change
};
#else
// Objects that are not owned by any thread use a thread id (tid) of zero.
Expand Down Expand Up @@ -173,7 +173,7 @@ struct _object {
#ifndef _Py_OPAQUE_PYOBJECT
struct PyVarObject {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
Py_ssize_t ob_size; // Number of items in variable part. Part of stable ABI
};
#endif
typedef struct PyVarObject PyVarObject;
Expand Down Expand Up @@ -265,56 +265,72 @@ _Py_IsOwnedByCurrentThread(PyObject *ob)
}
#endif

// Py_TYPE() implementation for the stable ABI
PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;

/* Definitions for the stable ABI */
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 14)
PyAPI_FUNC(PyTypeObject*) Py_TYPE(PyObject *ob);
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
PyAPI_FUNC(Py_ssize_t) Py_SIZE(PyObject *ob);
PyAPI_FUNC(int) Py_IS_TYPE(PyObject *ob, PyTypeObject *type);
PyAPI_FUNC(void) Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size);
#endif

#ifndef _Py_OPAQUE_PYOBJECT

#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030e0000
// Stable ABI implements Py_TYPE() as a function call
// on limited C API version 3.14 and newer.
static inline void
Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
{
ob->ob_type = type;
}

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 11)
// Non-limited API & limited API 3.11 & below: use static inline functions and
// use _PyObject_CAST so that users don't need their own casts
# define Py_TYPE(ob) _Py_TYPE_impl(_PyObject_CAST(ob))
# define Py_SIZE(ob) _Py_SIZE_impl(_PyObject_CAST(ob))
# define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl(_PyObject_CAST(ob), (type))
# define Py_SET_SIZE(ob, size) _Py_SET_SIZE_impl(_PyVarObject_CAST(ob), (size))
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#elif Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 15)
// Limited API 3.11-3.14: use static inline functions, without casts
# define Py_SIZE(ob) _Py_SIZE_impl(ob)
# define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl((ob), (type))
# define Py_SET_SIZE(ob, size) _Py_SET_SIZE_impl((ob), (size))
# if Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 14)
// Py_TYPE() is static inline only on Limited API 3.13 and below
# define Py_TYPE(ob) _Py_TYPE_impl(ob)
# endif
#else
static inline PyTypeObject* _Py_TYPE(PyObject *ob)
{
return ob->ob_type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST(ob))
#else
# define Py_TYPE(ob) _Py_TYPE(ob)
#endif
// Limited API 3.15+: use function calls
#endif

PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;
static inline
PyTypeObject* _Py_TYPE_impl(PyObject *ob)
{
return ob->ob_type;
}

#ifndef _Py_OPAQUE_PYOBJECT
// bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
static inline Py_ssize_t Py_SIZE(PyObject *ob) {
static inline Py_ssize_t
_Py_SIZE_impl(PyObject *ob)
{
assert(Py_TYPE(ob) != &PyLong_Type);
assert(Py_TYPE(ob) != &PyBool_Type);
return _PyVarObject_CAST(ob)->ob_size;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob))
#endif
#endif // !defined(_Py_OPAQUE_PYOBJECT)

static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
static inline int
_Py_IS_TYPE_impl(PyObject *ob, PyTypeObject *type)
{
return Py_TYPE(ob) == type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST(ob), (type))
#endif


#ifndef _Py_OPAQUE_PYOBJECT
static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
ob->ob_type = type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#endif

static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
static inline void
_Py_SET_SIZE_impl(PyVarObject *ob, Py_ssize_t size)
{
assert(Py_TYPE(_PyObject_CAST(ob)) != &PyLong_Type);
assert(Py_TYPE(_PyObject_CAST(ob)) != &PyBool_Type);
#ifdef Py_GIL_DISABLED
Expand All @@ -323,9 +339,7 @@ static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
ob->ob_size = size;
#endif
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_SIZE(ob, size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size))
#endif

#endif // !defined(_Py_OPAQUE_PYOBJECT)


Expand Down
4 changes: 4 additions & 0 deletions Lib/collections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,8 @@ def format_map(self, mapping):
return self.data.format_map(mapping)

def index(self, sub, start=0, end=_sys.maxsize):
if isinstance(sub, UserString):
sub = sub.data
return self.data.index(sub, start, end)

def isalpha(self):
Expand Down Expand Up @@ -1610,6 +1612,8 @@ def rfind(self, sub, start=0, end=_sys.maxsize):
return self.data.rfind(sub, start, end)

def rindex(self, sub, start=0, end=_sys.maxsize):
if isinstance(sub, UserString):
sub = sub.data
return self.data.rindex(sub, start, end)

def rjust(self, width, *args):
Expand Down
74 changes: 41 additions & 33 deletions Lib/test/string_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ def checkcall(self, obj, methodname, *args):
args = self.fixtype(args)
getattr(obj, methodname)(*args)

def _get_teststrings(self, charset, digits):
base = len(charset)
teststrings = set()
for i in range(base ** digits):
entry = []
for j in range(digits):
i, m = divmod(i, base)
entry.append(charset[m])
teststrings.add(''.join(entry))
teststrings = [self.fixtype(ts) for ts in teststrings]
return teststrings

def test_count(self):
self.checkequal(3, 'aaa', 'count', 'a')
self.checkequal(0, 'aaa', 'count', 'b')
Expand Down Expand Up @@ -130,17 +142,7 @@ def test_count(self):
# For a variety of combinations,
# verify that str.count() matches an equivalent function
# replacing all occurrences and then differencing the string lengths
charset = ['', 'a', 'b']
digits = 7
base = len(charset)
teststrings = set()
for i in range(base ** digits):
entry = []
for j in range(digits):
i, m = divmod(i, base)
entry.append(charset[m])
teststrings.add(''.join(entry))
teststrings = [self.fixtype(ts) for ts in teststrings]
teststrings = self._get_teststrings(['', 'a', 'b'], 7)
for i in teststrings:
n = len(i)
for j in teststrings:
Expand Down Expand Up @@ -197,17 +199,7 @@ def test_find(self):
# For a variety of combinations,
# verify that str.find() matches __contains__
# and that the found substring is really at that location
charset = ['', 'a', 'b', 'c']
digits = 5
base = len(charset)
teststrings = set()
for i in range(base ** digits):
entry = []
for j in range(digits):
i, m = divmod(i, base)
entry.append(charset[m])
teststrings.add(''.join(entry))
teststrings = [self.fixtype(ts) for ts in teststrings]
teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5)
for i in teststrings:
for j in teststrings:
loc = i.find(j)
Expand Down Expand Up @@ -244,17 +236,7 @@ def test_rfind(self):
# For a variety of combinations,
# verify that str.rfind() matches __contains__
# and that the found substring is really at that location
charset = ['', 'a', 'b', 'c']
digits = 5
base = len(charset)
teststrings = set()
for i in range(base ** digits):
entry = []
for j in range(digits):
i, m = divmod(i, base)
entry.append(charset[m])
teststrings.add(''.join(entry))
teststrings = [self.fixtype(ts) for ts in teststrings]
teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5)
for i in teststrings:
for j in teststrings:
loc = i.rfind(j)
Expand Down Expand Up @@ -295,6 +277,19 @@ def test_index(self):
else:
self.checkraises(TypeError, 'hello', 'index', 42)

# For a variety of combinations,
# verify that str.index() matches __contains__
# and that the found substring is really at that location
teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5)
for i in teststrings:
for j in teststrings:
if j in i:
loc = i.index(j)
self.assertGreaterEqual(loc, 0)
self.assertEqual(i[loc:loc+len(j)], j)
else:
self.assertRaises(ValueError, i.index, j)

def test_rindex(self):
self.checkequal(12, 'abcdefghiabc', 'rindex', '')
self.checkequal(3, 'abcdefghiabc', 'rindex', 'def')
Expand All @@ -321,6 +316,19 @@ def test_rindex(self):
else:
self.checkraises(TypeError, 'hello', 'rindex', 42)

# For a variety of combinations,
# verify that str.rindex() matches __contains__
# and that the found substring is really at that location
teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5)
for i in teststrings:
for j in teststrings:
if j in i:
loc = i.rindex(j)
self.assertGreaterEqual(loc, 0)
self.assertEqual(i[loc:loc+len(j)], j)
else:
self.assertRaises(ValueError, i.rindex, j)

def test_find_periodic_pattern(self):
"""Cover the special path for periodic patterns."""
def reference_find(p, s):
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -3385,6 +3385,7 @@ MODULE__DECIMAL_DEPS=@LIBMPDEC_INTERNAL@
MODULE__ELEMENTTREE_DEPS=$(srcdir)/Modules/pyexpat.c @LIBEXPAT_INTERNAL@
MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h
MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h
MODULE__REMOTE_DEBUGGING_DEPS=$(srcdir)/Modules/_remote_debugging/_remote_debugging.h

# HACL*-based cryptographic primitives
MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_MD5_HEADERS) $(LIBHACL_MD5_LIB_@LIBHACL_LDEPS_LIBTYPE@)
Expand Down
Loading
Loading