cpython/Objects/structseq.c

675 lines
19 KiB
C
Raw Normal View History

/* Implementation helper: a struct that looks like a tuple.
See timemodule and posixmodule for example uses.
The structseq helper is considered an internal CPython implementation
detail. Docs for modules using structseqs should call them
"named tuples" (be sure to include a space between the two
words and add a link back to the term in Docs/glossary.rst).
*/
#include "Python.h"
#include "pycore_tuple.h" // _PyTuple_FromArray()
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "structmember.h" // PyMemberDef
#include "pycore_structseq.h" // PyStructSequence_InitType()
#include "pycore_initconfig.h" // _PyStatus_OK()
static const char visible_length_key[] = "n_sequence_fields";
static const char real_length_key[] = "n_fields";
static const char unnamed_fields_key[] = "n_unnamed_fields";
static const char match_args_key[] = "__match_args__";
/* Fields with this name have only a field index, not a field name.
They are only allowed for indices < n_visible_fields. */
const char * const PyStructSequence_UnnamedField = "unnamed field";
static Py_ssize_t
bpo-46541: Replace core use of _Py_IDENTIFIER() with statically initialized global objects. (gh-30928) We're no longer using _Py_IDENTIFIER() (or _Py_static_string()) in any core CPython code. It is still used in a number of non-builtin stdlib modules. The replacement is: PyUnicodeObject (not pointer) fields under _PyRuntimeState, statically initialized as part of _PyRuntime. A new _Py_GET_GLOBAL_IDENTIFIER() macro facilitates lookup of the fields (along with _Py_GET_GLOBAL_STRING() for non-identifier strings). https://bugs.python.org/issue46541#msg411799 explains the rationale for this change. The core of the change is in: * (new) Include/internal/pycore_global_strings.h - the declarations for the global strings, along with the macros * Include/internal/pycore_runtime_init.h - added the static initializers for the global strings * Include/internal/pycore_global_objects.h - where the struct in pycore_global_strings.h is hooked into _PyRuntimeState * Tools/scripts/generate_global_objects.py - added generation of the global string declarations and static initializers I've also added a --check flag to generate_global_objects.py (along with make check-global-objects) to check for unused global strings. That check is added to the PR CI config. The remainder of this change updates the core code to use _Py_GET_GLOBAL_IDENTIFIER() instead of _Py_IDENTIFIER() and the related _Py*Id functions (likewise for _Py_GET_GLOBAL_STRING() instead of _Py_static_string()). This includes adding a few functions where there wasn't already an alternative to _Py*Id(), replacing the _Py_Identifier * parameter with PyObject *. The following are not changed (yet): * stop using _Py_IDENTIFIER() in the stdlib modules * (maybe) get rid of _Py_IDENTIFIER(), etc. entirely -- this may not be doable as at least one package on PyPI using this (private) API * (maybe) intern the strings during runtime init https://bugs.python.org/issue46541
2022-02-08 16:39:07 -04:00
get_type_attr_as_size(PyTypeObject *tp, PyObject *name)
{
PyObject *v = PyDict_GetItemWithError(tp->tp_dict, name);
if (v == NULL && !PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError,
"Missed attribute '%U' of type %s",
name, tp->tp_name);
return -1;
}
return PyLong_AsSsize_t(v);
}
#define VISIBLE_SIZE(op) Py_SIZE(op)
bpo-46541: Replace core use of _Py_IDENTIFIER() with statically initialized global objects. (gh-30928) We're no longer using _Py_IDENTIFIER() (or _Py_static_string()) in any core CPython code. It is still used in a number of non-builtin stdlib modules. The replacement is: PyUnicodeObject (not pointer) fields under _PyRuntimeState, statically initialized as part of _PyRuntime. A new _Py_GET_GLOBAL_IDENTIFIER() macro facilitates lookup of the fields (along with _Py_GET_GLOBAL_STRING() for non-identifier strings). https://bugs.python.org/issue46541#msg411799 explains the rationale for this change. The core of the change is in: * (new) Include/internal/pycore_global_strings.h - the declarations for the global strings, along with the macros * Include/internal/pycore_runtime_init.h - added the static initializers for the global strings * Include/internal/pycore_global_objects.h - where the struct in pycore_global_strings.h is hooked into _PyRuntimeState * Tools/scripts/generate_global_objects.py - added generation of the global string declarations and static initializers I've also added a --check flag to generate_global_objects.py (along with make check-global-objects) to check for unused global strings. That check is added to the PR CI config. The remainder of this change updates the core code to use _Py_GET_GLOBAL_IDENTIFIER() instead of _Py_IDENTIFIER() and the related _Py*Id functions (likewise for _Py_GET_GLOBAL_STRING() instead of _Py_static_string()). This includes adding a few functions where there wasn't already an alternative to _Py*Id(), replacing the _Py_Identifier * parameter with PyObject *. The following are not changed (yet): * stop using _Py_IDENTIFIER() in the stdlib modules * (maybe) get rid of _Py_IDENTIFIER(), etc. entirely -- this may not be doable as at least one package on PyPI using this (private) API * (maybe) intern the strings during runtime init https://bugs.python.org/issue46541
2022-02-08 16:39:07 -04:00
#define VISIBLE_SIZE_TP(tp) \
get_type_attr_as_size(tp, &_Py_ID(n_sequence_fields))
#define REAL_SIZE_TP(tp) \
get_type_attr_as_size(tp, &_Py_ID(n_fields))
#define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op))
bpo-46541: Replace core use of _Py_IDENTIFIER() with statically initialized global objects. (gh-30928) We're no longer using _Py_IDENTIFIER() (or _Py_static_string()) in any core CPython code. It is still used in a number of non-builtin stdlib modules. The replacement is: PyUnicodeObject (not pointer) fields under _PyRuntimeState, statically initialized as part of _PyRuntime. A new _Py_GET_GLOBAL_IDENTIFIER() macro facilitates lookup of the fields (along with _Py_GET_GLOBAL_STRING() for non-identifier strings). https://bugs.python.org/issue46541#msg411799 explains the rationale for this change. The core of the change is in: * (new) Include/internal/pycore_global_strings.h - the declarations for the global strings, along with the macros * Include/internal/pycore_runtime_init.h - added the static initializers for the global strings * Include/internal/pycore_global_objects.h - where the struct in pycore_global_strings.h is hooked into _PyRuntimeState * Tools/scripts/generate_global_objects.py - added generation of the global string declarations and static initializers I've also added a --check flag to generate_global_objects.py (along with make check-global-objects) to check for unused global strings. That check is added to the PR CI config. The remainder of this change updates the core code to use _Py_GET_GLOBAL_IDENTIFIER() instead of _Py_IDENTIFIER() and the related _Py*Id functions (likewise for _Py_GET_GLOBAL_STRING() instead of _Py_static_string()). This includes adding a few functions where there wasn't already an alternative to _Py*Id(), replacing the _Py_Identifier * parameter with PyObject *. The following are not changed (yet): * stop using _Py_IDENTIFIER() in the stdlib modules * (maybe) get rid of _Py_IDENTIFIER(), etc. entirely -- this may not be doable as at least one package on PyPI using this (private) API * (maybe) intern the strings during runtime init https://bugs.python.org/issue46541
2022-02-08 16:39:07 -04:00
#define UNNAMED_FIELDS_TP(tp) \
get_type_attr_as_size(tp, &_Py_ID(n_unnamed_fields))
#define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP(Py_TYPE(op))
2002-10-16 16:10:03 -03:00
PyObject *
PyStructSequence_New(PyTypeObject *type)
{
PyStructSequence *obj;
Py_ssize_t size = REAL_SIZE_TP(type), i;
if (size < 0) {
return NULL;
}
Py_ssize_t vsize = VISIBLE_SIZE_TP(type);
if (vsize < 0) {
return NULL;
}
Merged revisions 59933-59951 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59935 | raymond.hettinger | 2008-01-13 07:15:15 +0100 (Sun, 13 Jan 2008) | 1 line Named tuple is a concept, not a specific type. ........ r59936 | raymond.hettinger | 2008-01-13 07:18:07 +0100 (Sun, 13 Jan 2008) | 1 line Fix spelling. ........ r59937 | georg.brandl | 2008-01-13 10:36:18 +0100 (Sun, 13 Jan 2008) | 2 lines Clarify the effect of text mode. ........ r59938 | thomas.heller | 2008-01-13 12:19:43 +0100 (Sun, 13 Jan 2008) | 1 line Make Modules/socketobject.c compile for Windows again. ........ r59939 | ka-ping.yee | 2008-01-13 12:25:13 +0100 (Sun, 13 Jan 2008) | 9 lines Check in the patch proposed by Ben Hayden (benjhayden) for issue #1550: help('modules') broken by several 3rd party libraries. Tested with Python build: trunk:54235:59936M -- the reported error occurs with Django installed (or with any __init__.py present on the path that raises an exception), and such errors indeed go away when this change is applied. ........ r59940 | georg.brandl | 2008-01-13 16:04:05 +0100 (Sun, 13 Jan 2008) | 2 lines Back out r59931 - test_ctypes fails with it. ........ r59943 | amaury.forgeotdarc | 2008-01-14 01:22:44 +0100 (Mon, 14 Jan 2008) | 6 lines As discussed in issue 1700288: ctypes takes some liberties when creating python types: it modifies the types' __dict__ directly, bypassing all the machinery of type objects which deal with special methods. And this broke recent optimisations of method lookup. Now we try to modify the type with more "official" functions. ........ r59944 | amaury.forgeotdarc | 2008-01-14 01:29:41 +0100 (Mon, 14 Jan 2008) | 5 lines Re-apply patch #1700288 (first applied in r59931, rolled back in r59940) now that ctypes uses a more supported method to create types: Method cache optimization, by Armin Rigo, ported to 2.6 by Kevin Jacobs. ........ r59946 | amaury.forgeotdarc | 2008-01-14 02:07:27 +0100 (Mon, 14 Jan 2008) | 4 lines ?Why did my tests not notice this before? Slots inheritance is very different from OO inheritance. This code lead to infinite recursion on classes derived from StructType. ........ r59947 | christian.heimes | 2008-01-14 04:33:52 +0100 (Mon, 14 Jan 2008) | 1 line Added new an better structseq representation. E.g. repr(time.gmtime(0)) now returns 'time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)' instead of '(1970, 1, 1, 0, 0, 0, 3, 1, 0)'. The feature is part of #1816: sys.flags ........ r59948 | christian.heimes | 2008-01-14 04:35:38 +0100 (Mon, 14 Jan 2008) | 1 line I missed the most important file ........ r59949 | christian.heimes | 2008-01-14 04:42:48 +0100 (Mon, 14 Jan 2008) | 1 line Applied patch #1816: sys.flags patch ........ r59950 | christian.heimes | 2008-01-14 05:13:37 +0100 (Mon, 14 Jan 2008) | 2 lines Now that I've learnt about structseq objects I felt like converting sys.float_info to a structseq. It's readonly and help(sys.float_info) explains the attributes nicely. ........ r59951 | christian.heimes | 2008-01-14 07:06:19 +0100 (Mon, 14 Jan 2008) | 1 line Added more comments to the new structseq repr code and implemented several of Neal's suggestions. ........
2008-01-14 14:49:24 -04:00
obj = PyObject_GC_NewVar(PyStructSequence, type, size);
if (obj == NULL)
return NULL;
/* Hack the size of the variable object, so invisible fields don't appear
to Python code. */
Py_SET_SIZE(obj, vsize);
for (i = 0; i < size; i++)
obj->ob_item[i] = NULL;
return (PyObject*)obj;
Merge the trunk changes in. Breaks socket.ssl for now. Merged revisions 57392-57619 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r57395 | georg.brandl | 2007-08-24 19:23:23 +0200 (Fri, 24 Aug 2007) | 2 lines Bug #1011: fix rfc822.Message.getheader docs. ........ r57397 | georg.brandl | 2007-08-24 19:38:49 +0200 (Fri, 24 Aug 2007) | 2 lines Patch #1006: port test_winreg to unittest. ........ r57398 | georg.brandl | 2007-08-24 19:46:54 +0200 (Fri, 24 Aug 2007) | 2 lines Fix #1012: wrong URL to :mod:`site` in install/index.rst. ........ r57399 | georg.brandl | 2007-08-24 20:07:52 +0200 (Fri, 24 Aug 2007) | 2 lines Patch #1008: port test_signal to unittest. ........ r57400 | georg.brandl | 2007-08-24 20:22:54 +0200 (Fri, 24 Aug 2007) | 2 lines Port test_frozen to unittest. ........ r57401 | georg.brandl | 2007-08-24 20:27:43 +0200 (Fri, 24 Aug 2007) | 2 lines Document new utility functions in test_support. ........ r57402 | georg.brandl | 2007-08-24 20:30:06 +0200 (Fri, 24 Aug 2007) | 2 lines Remove test_rgbimg output file, there is no test_rgbimg.py. ........ r57403 | georg.brandl | 2007-08-24 20:35:27 +0200 (Fri, 24 Aug 2007) | 2 lines Remove output file for test_ossaudiodev, also properly close the dsp object. ........ r57404 | georg.brandl | 2007-08-24 20:46:27 +0200 (Fri, 24 Aug 2007) | 2 lines Convert test_linuxaudiodev to unittest. Fix a wrong finally clause in test_ossaudiodev. ........ r57406 | collin.winter | 2007-08-24 21:13:58 +0200 (Fri, 24 Aug 2007) | 1 line Convert test_pkg to use unittest. ........ r57408 | georg.brandl | 2007-08-24 21:22:34 +0200 (Fri, 24 Aug 2007) | 2 lines Catch the correct errors. ........ r57409 | georg.brandl | 2007-08-24 21:33:53 +0200 (Fri, 24 Aug 2007) | 2 lines Port test_class to unittest. Patch #1671298. ........ r57415 | collin.winter | 2007-08-24 23:09:42 +0200 (Fri, 24 Aug 2007) | 1 line Make test_structmembers pass when run with regrtests's -R flag. ........ r57455 | nick.coghlan | 2007-08-25 06:32:07 +0200 (Sat, 25 Aug 2007) | 1 line Revert misguided attempt at fixing incompatibility between -m and -i switches (better fix coming soon) ........ r57456 | nick.coghlan | 2007-08-25 06:35:54 +0200 (Sat, 25 Aug 2007) | 1 line Revert compile.c changes that shouldn't have been included in previous checkin ........ r57461 | nick.coghlan | 2007-08-25 12:50:41 +0200 (Sat, 25 Aug 2007) | 1 line Fix bug 1764407 - the -i switch now does the right thing when using the -m switch ........ r57464 | guido.van.rossum | 2007-08-25 17:08:43 +0200 (Sat, 25 Aug 2007) | 4 lines Server-side SSL and certificate validation, by Bill Janssen. While cleaning up Bill's C style, I may have cleaned up some code he didn't touch as well (in _ssl.c). ........ r57465 | neal.norwitz | 2007-08-25 18:41:36 +0200 (Sat, 25 Aug 2007) | 3 lines Try to get this to build with Visual Studio by moving all the variable declarations to the beginning of a scope. ........ r57466 | neal.norwitz | 2007-08-25 18:54:38 +0200 (Sat, 25 Aug 2007) | 1 line Fix test so it is skipped properly if there is no SSL support. ........ r57467 | neal.norwitz | 2007-08-25 18:58:09 +0200 (Sat, 25 Aug 2007) | 2 lines Fix a few more variables to try to get this to compile with Visual Studio. ........ r57473 | neal.norwitz | 2007-08-25 19:25:17 +0200 (Sat, 25 Aug 2007) | 1 line Try to get this test to pass for systems that do not have SO_REUSEPORT ........ r57482 | gregory.p.smith | 2007-08-26 02:26:00 +0200 (Sun, 26 Aug 2007) | 7 lines keep setup.py from listing unneeded hash modules (_md5, _sha*) as missing when they were not built because _hashlib with openssl provided their functionality instead. don't build bsddb185 if bsddb was built. ........ r57483 | neal.norwitz | 2007-08-26 03:08:16 +0200 (Sun, 26 Aug 2007) | 1 line Fix typo in docstring (missing c in reacquire) ........ r57484 | neal.norwitz | 2007-08-26 03:42:03 +0200 (Sun, 26 Aug 2007) | 2 lines Spell check (also americanify behaviour, it's almost 3 times as common) ........ r57503 | neal.norwitz | 2007-08-26 08:29:57 +0200 (Sun, 26 Aug 2007) | 4 lines Reap children before the test starts so hopefully SocketServer won't find any old children left around which causes an exception in collect_children() and the test to fail. ........ r57510 | neal.norwitz | 2007-08-26 20:50:39 +0200 (Sun, 26 Aug 2007) | 1 line Fail gracefully if the cert files cannot be created ........ r57513 | guido.van.rossum | 2007-08-26 21:35:09 +0200 (Sun, 26 Aug 2007) | 4 lines Bill Janssen wrote: Here's a patch which makes test_ssl a better player in the buildbots environment. I deep-ended on "try-except-else" clauses. ........ r57518 | neal.norwitz | 2007-08-26 23:40:16 +0200 (Sun, 26 Aug 2007) | 1 line Get the test passing by commenting out some writes (should they be removed?) ........ r57522 | neal.norwitz | 2007-08-27 00:16:23 +0200 (Mon, 27 Aug 2007) | 3 lines Catch IOError for when the device file doesn't exist or the user doesn't have permission to write to the device. ........ r57524 | neal.norwitz | 2007-08-27 00:20:03 +0200 (Mon, 27 Aug 2007) | 5 lines Another patch from Bill Janssen that: 1) Fixes the bug that two class names are initial-lower-case. 2) Replaces the poll waiting for the server to become ready with a threading.Event signal. ........ r57536 | neal.norwitz | 2007-08-27 02:58:33 +0200 (Mon, 27 Aug 2007) | 1 line Stop using string.join (from the module) to ease upgrade to py3k ........ r57537 | neal.norwitz | 2007-08-27 03:03:18 +0200 (Mon, 27 Aug 2007) | 1 line Make a utility function for handling (printing) an error ........ r57538 | neal.norwitz | 2007-08-27 03:15:33 +0200 (Mon, 27 Aug 2007) | 4 lines If we can't create a certificate, print a warning, but don't fail the test. Modified patch from what Bill Janssen sent on python-3000. ........ r57539 | facundo.batista | 2007-08-27 03:15:34 +0200 (Mon, 27 Aug 2007) | 7 lines Ignore test failures caused by 'resource temporarily unavailable' exceptions raised in the test server thread, since SimpleXMLRPCServer does not gracefully handle them. Changed number of requests handled by tests server thread to one (was 2) because no tests require more than one request. [GSoC - Alan McIntyre] ........ r57561 | guido.van.rossum | 2007-08-27 19:19:42 +0200 (Mon, 27 Aug 2007) | 8 lines > Regardless, building a fixed test certificate and checking it in sounds like > the better option. Then the openssl command in the test code can be turned > into a comment describing how the test data was pregenerated. Here's a patch that does that. Bill ........ r57568 | guido.van.rossum | 2007-08-27 20:42:23 +0200 (Mon, 27 Aug 2007) | 26 lines > Some of the code sets the error string in this directly before > returning NULL, and other pieces of the code call PySSL_SetError, > which creates the error string. I think some of the places which set > the string directly probably shouldn't; instead, they should call > PySSL_SetError to cons up the error name directly from the err code. > However, PySSL_SetError only works after the construction of an ssl > object, which means it can't be used there... I'll take a longer look > at it and see if there's a reasonable fix. Here's a patch which addresses this. It also fixes the indentation in PySSL_SetError, bringing it into line with PEP 7, fixes a compile warning about one of the OpenSSL macros, and makes the namespace a bit more consistent. I've tested it on FC 7 and OS X 10.4. % ./python ./Lib/test/regrtest.py -R :1: -u all test_ssl test_ssl beginning 6 repetitions 123456 ...... 1 test OK. [29244 refs] % [GvR: slightly edited to enforce 79-char line length, even if it required violating the style guide.] ........ r57570 | guido.van.rossum | 2007-08-27 21:11:11 +0200 (Mon, 27 Aug 2007) | 2 lines Patch 10124 by Bill Janssen, docs for the new ssl code. ........ r57574 | guido.van.rossum | 2007-08-27 22:51:00 +0200 (Mon, 27 Aug 2007) | 3 lines Patch # 1739906 by Christian Heimes -- add reduce to functools (importing it from __builtin__). ........ r57575 | guido.van.rossum | 2007-08-27 22:52:10 +0200 (Mon, 27 Aug 2007) | 2 lines News about functools.reduce. ........ r57611 | georg.brandl | 2007-08-28 10:29:08 +0200 (Tue, 28 Aug 2007) | 2 lines Document rev. 57574. ........ r57612 | sean.reifschneider | 2007-08-28 11:07:54 +0200 (Tue, 28 Aug 2007) | 2 lines Adding basic imputil documentation. ........ r57614 | georg.brandl | 2007-08-28 12:48:18 +0200 (Tue, 28 Aug 2007) | 2 lines Fix some glitches. ........ r57616 | lars.gustaebel | 2007-08-28 14:31:09 +0200 (Tue, 28 Aug 2007) | 5 lines TarFile.__init__() no longer fails if no name argument is passed and the fileobj argument has no usable name attribute (e.g. StringIO). (will backport to 2.5) ........ r57619 | thomas.wouters | 2007-08-28 17:28:19 +0200 (Tue, 28 Aug 2007) | 22 lines Improve extended slicing support in builtin types and classes. Specifically: - Specialcase extended slices that amount to a shallow copy the same way as is done for simple slices, in the tuple, string and unicode case. - Specialcase step-1 extended slices to optimize the common case for all involved types. - For lists, allow extended slice assignment of differing lengths as long as the step is 1. (Previously, 'l[:2:1] = []' failed even though 'l[:2] = []' and 'l[:2:None] = []' do not.) - Implement extended slicing for buffer, array, structseq, mmap and UserString.UserString. - Implement slice-object support (but not non-step-1 slice assignment) for UserString.MutableString. - Add tests for all new functionality. ........
2007-08-28 18:37:11 -03:00
}
2010-12-03 16:14:31 -04:00
void
PyStructSequence_SetItem(PyObject* op, Py_ssize_t i, PyObject* v)
{
PyStructSequence_SET_ITEM(op, i, v);
}
PyObject*
PyStructSequence_GetItem(PyObject* op, Py_ssize_t i)
{
return PyStructSequence_GET_ITEM(op, i);
}
static int
structseq_traverse(PyStructSequence *obj, visitproc visit, void *arg)
{
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_HEAPTYPE) {
Py_VISIT(Py_TYPE(obj));
}
Py_ssize_t i, size;
size = REAL_SIZE(obj);
for (i = 0; i < size; ++i) {
Py_VISIT(obj->ob_item[i]);
}
return 0;
}
static void
structseq_dealloc(PyStructSequence *obj)
{
Py_ssize_t i, size;
PyObject_GC_UnTrack(obj);
PyTypeObject *tp = Py_TYPE(obj);
size = REAL_SIZE(obj);
for (i = 0; i < size; ++i) {
Py_XDECREF(obj->ob_item[i]);
}
PyObject_GC_Del(obj);
if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
Py_DECREF(tp);
}
}
/*[clinic input]
class structseq "PyStructSequence *" "NULL"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9d781c6922c77752]*/
#include "clinic/structseq.c.h"
/*[clinic input]
@classmethod
structseq.__new__ as structseq_new
sequence as arg: object
dict: object(c_default="NULL") = {}
[clinic start generated code]*/
static PyObject *
structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict)
/*[clinic end generated code: output=baa082e788b171da input=90532511101aa3fb]*/
{
PyObject *ob;
PyStructSequence *res = NULL;
Py_ssize_t len, min_len, max_len, i, n_unnamed_fields;
min_len = VISIBLE_SIZE_TP(type);
if (min_len < 0) {
return NULL;
}
max_len = REAL_SIZE_TP(type);
if (max_len < 0) {
return NULL;
}
n_unnamed_fields = UNNAMED_FIELDS_TP(type);
if (n_unnamed_fields < 0) {
return NULL;
}
arg = PySequence_Fast(arg, "constructor requires a sequence");
if (!arg) {
return NULL;
}
if (dict && !PyDict_Check(dict)) {
PyErr_Format(PyExc_TypeError,
"%.500s() takes a dict as second arg, if any",
type->tp_name);
Py_DECREF(arg);
return NULL;
}
len = PySequence_Fast_GET_SIZE(arg);
if (min_len != max_len) {
if (len < min_len) {
PyErr_Format(PyExc_TypeError,
2012-02-14 21:52:58 -04:00
"%.500s() takes an at least %zd-sequence (%zd-sequence given)",
type->tp_name, min_len, len);
Py_DECREF(arg);
return NULL;
}
if (len > max_len) {
PyErr_Format(PyExc_TypeError,
2012-02-14 21:52:58 -04:00
"%.500s() takes an at most %zd-sequence (%zd-sequence given)",
type->tp_name, max_len, len);
Py_DECREF(arg);
return NULL;
}
}
else {
if (len != min_len) {
PyErr_Format(PyExc_TypeError,
2012-02-14 21:52:58 -04:00
"%.500s() takes a %zd-sequence (%zd-sequence given)",
type->tp_name, min_len, len);
Py_DECREF(arg);
return NULL;
}
}
res = (PyStructSequence*) PyStructSequence_New(type);
if (res == NULL) {
Py_DECREF(arg);
return NULL;
}
for (i = 0; i < len; ++i) {
PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
res->ob_item[i] = Py_NewRef(v);
}
Py_DECREF(arg);
for (; i < max_len; ++i) {
if (dict == NULL) {
ob = Py_None;
}
else {
ob = _PyDict_GetItemStringWithError(dict,
type->tp_members[i-n_unnamed_fields].name);
if (ob == NULL) {
if (PyErr_Occurred()) {
Py_DECREF(res);
return NULL;
}
ob = Py_None;
}
}
res->ob_item[i] = Py_NewRef(ob);
}
_PyObject_GC_TRACK(res);
return (PyObject*) res;
}
static PyObject *
structseq_repr(PyStructSequence *obj)
{
PyTypeObject *typ = Py_TYPE(obj);
_PyUnicodeWriter writer;
/* Write "typename(" */
PyObject *type_name = PyUnicode_DecodeUTF8(typ->tp_name,
strlen(typ->tp_name),
NULL);
if (type_name == NULL) {
return NULL;
}
_PyUnicodeWriter_Init(&writer);
writer.overallocate = 1;
/* count 5 characters per item: "x=1, " */
writer.min_length = (PyUnicode_GET_LENGTH(type_name) + 1
+ VISIBLE_SIZE(obj) * 5 + 1);
if (_PyUnicodeWriter_WriteStr(&writer, type_name) < 0) {
Py_DECREF(type_name);
goto error;
}
Py_DECREF(type_name);
if (_PyUnicodeWriter_WriteChar(&writer, '(') < 0) {
goto error;
}
for (Py_ssize_t i=0; i < VISIBLE_SIZE(obj); i++) {
if (i > 0) {
/* Write ", " */
if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) {
goto error;
}
}
/* Write "name=repr" */
const char *name_utf8 = typ->tp_members[i].name;
if (name_utf8 == NULL) {
PyErr_Format(PyExc_SystemError, "In structseq_repr(), member %zd name is NULL"
" for type %.500s", i, typ->tp_name);
goto error;
}
PyObject *name = PyUnicode_DecodeUTF8(name_utf8, strlen(name_utf8), NULL);
if (name == NULL) {
goto error;
}
if (_PyUnicodeWriter_WriteStr(&writer, name) < 0) {
Py_DECREF(name);
goto error;
}
Py_DECREF(name);
if (_PyUnicodeWriter_WriteChar(&writer, '=') < 0) {
goto error;
}
PyObject *value = PyStructSequence_GET_ITEM(obj, i);
assert(value != NULL);
PyObject *repr = PyObject_Repr(value);
if (repr == NULL) {
goto error;
}
if (_PyUnicodeWriter_WriteStr(&writer, repr) < 0) {
Py_DECREF(repr);
goto error;
}
Py_DECREF(repr);
}
if (_PyUnicodeWriter_WriteChar(&writer, ')') < 0) {
goto error;
}
return _PyUnicodeWriter_Finish(&writer);
error:
_PyUnicodeWriter_Dealloc(&writer);
return NULL;
}
static PyObject *
structseq_reduce(PyStructSequence* self, PyObject *Py_UNUSED(ignored))
{
PyObject* tup = NULL;
PyObject* dict = NULL;
PyObject* result;
Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields, i;
n_fields = REAL_SIZE(self);
if (n_fields < 0) {
return NULL;
}
n_visible_fields = VISIBLE_SIZE(self);
n_unnamed_fields = UNNAMED_FIELDS(self);
if (n_unnamed_fields < 0) {
return NULL;
}
tup = _PyTuple_FromArray(self->ob_item, n_visible_fields);
if (!tup)
goto error;
dict = PyDict_New();
if (!dict)
goto error;
for (i = n_visible_fields; i < n_fields; i++) {
const char *n = Py_TYPE(self)->tp_members[i-n_unnamed_fields].name;
if (PyDict_SetItemString(dict, n, self->ob_item[i]) < 0)
goto error;
}
result = Py_BuildValue("(O(OO))", Py_TYPE(self), tup, dict);
Py_DECREF(tup);
Py_DECREF(dict);
return result;
error:
Py_XDECREF(tup);
Py_XDECREF(dict);
return NULL;
}
static PyMethodDef structseq_methods[] = {
{"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
{NULL, NULL}
};
static Py_ssize_t
count_members(PyStructSequence_Desc *desc, Py_ssize_t *n_unnamed_members) {
Py_ssize_t i;
*n_unnamed_members = 0;
for (i = 0; desc->fields[i].name != NULL; ++i) {
if (desc->fields[i].name == PyStructSequence_UnnamedField) {
(*n_unnamed_members)++;
}
}
return i;
}
static int
initialize_structseq_dict(PyStructSequence_Desc *desc, PyObject* dict,
Py_ssize_t n_members, Py_ssize_t n_unnamed_members) {
PyObject *v;
#define SET_DICT_FROM_SIZE(key, value) \
do { \
v = PyLong_FromSsize_t(value); \
if (v == NULL) { \
return -1; \
} \
if (PyDict_SetItemString(dict, key, v) < 0) { \
Py_DECREF(v); \
return -1; \
} \
Py_DECREF(v); \
} while (0)
SET_DICT_FROM_SIZE(visible_length_key, desc->n_in_sequence);
SET_DICT_FROM_SIZE(real_length_key, n_members);
SET_DICT_FROM_SIZE(unnamed_fields_key, n_unnamed_members);
// Prepare and set __match_args__
Py_ssize_t i, k;
PyObject* keys = PyTuple_New(desc->n_in_sequence);
if (keys == NULL) {
return -1;
}
for (i = k = 0; i < desc->n_in_sequence; ++i) {
if (desc->fields[i].name == PyStructSequence_UnnamedField) {
continue;
}
PyObject* new_member = PyUnicode_FromString(desc->fields[i].name);
if (new_member == NULL) {
goto error;
}
PyTuple_SET_ITEM(keys, k, new_member);
k++;
}
if (_PyTuple_Resize(&keys, k) == -1) {
goto error;
}
if (PyDict_SetItemString(dict, match_args_key, keys) < 0) {
goto error;
}
Py_DECREF(keys);
return 0;
error:
Py_DECREF(keys);
return -1;
}
static PyMemberDef *
initialize_members(PyStructSequence_Desc *desc,
Py_ssize_t *pn_members, Py_ssize_t *pn_unnamed_members)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;
n_members = count_members(desc, &n_unnamed_members);
members = PyMem_NEW(PyMemberDef, n_members - n_unnamed_members + 1);
if (members == NULL) {
PyErr_NoMemory();
return NULL;
}
Py_ssize_t i, k;
for (i = k = 0; i < n_members; ++i) {
if (desc->fields[i].name == PyStructSequence_UnnamedField) {
continue;
}
/* The names and docstrings in these MemberDefs are statically */
/* allocated so it is expected that they'll outlive the MemberDef */
members[k].name = desc->fields[i].name;
members[k].type = T_OBJECT;
members[k].offset = offsetof(PyStructSequence, ob_item)
+ i * sizeof(PyObject*);
members[k].flags = READONLY;
members[k].doc = desc->fields[i].doc;
k++;
}
members[k].name = NULL;
*pn_members = n_members;
*pn_unnamed_members = n_unnamed_members;
return members;
}
static void
initialize_static_fields(PyTypeObject *type, PyStructSequence_Desc *desc,
PyMemberDef *tp_members, unsigned long tp_flags)
{
type->tp_name = desc->name;
type->tp_basicsize = sizeof(PyStructSequence) - sizeof(PyObject *);
type->tp_itemsize = sizeof(PyObject *);
type->tp_dealloc = (destructor)structseq_dealloc;
type->tp_repr = (reprfunc)structseq_repr;
type->tp_doc = desc->doc;
type->tp_base = &PyTuple_Type;
type->tp_methods = structseq_methods;
type->tp_new = structseq_new;
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
type->tp_traverse = (traverseproc) structseq_traverse;
type->tp_members = tp_members;
}
static int
initialize_static_type(PyTypeObject *type, PyStructSequence_Desc *desc,
Py_ssize_t n_members, Py_ssize_t n_unnamed_members) {
/* initialize_static_fields() should have been called already. */
if (PyType_Ready(type) < 0) {
return -1;
}
Py_INCREF(type);
if (initialize_structseq_dict(
desc, type->tp_dict, n_members, n_unnamed_members) < 0) {
Py_DECREF(type);
return -1;
}
return 0;
}
int
_PyStructSequence_InitBuiltinWithFlags(PyTypeObject *type,
PyStructSequence_Desc *desc,
unsigned long tp_flags)
{
if (type->tp_flags & Py_TPFLAGS_READY) {
if (_PyStaticType_InitBuiltin(type) < 0) {
goto failed_init_builtin;
}
return 0;
}
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;
members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
return -1;
}
initialize_static_fields(type, desc, members, tp_flags);
Py_INCREF(type); // XXX It should be immortal.
if (_PyStaticType_InitBuiltin(type) < 0) {
PyMem_Free(members);
goto failed_init_builtin;
}
if (initialize_structseq_dict(
desc, type->tp_dict, n_members, n_unnamed_members) < 0) {
PyMem_Free(members);
return -1;
}
return 0;
failed_init_builtin:
PyErr_Format(PyExc_RuntimeError,
"Can't initialize builtin type %s",
desc->name);
return -1;
}
int
PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;
#ifdef Py_TRACE_REFS
/* if the type object was chained, unchain it first
before overwriting its storage */
if (type->ob_base.ob_base._ob_next) {
_Py_ForgetReference((PyObject *)type);
}
#endif
/* PyTypeObject has already been initialized */
if (Py_REFCNT(type) != 0) {
PyErr_BadInternalCall();
return -1;
}
members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
return -1;
}
initialize_static_fields(type, desc, members, 0);
if (initialize_static_type(type, desc, n_members, n_unnamed_members) < 0) {
PyMem_Free(members);
return -1;
}
return 0;
}
void
PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
{
(void)PyStructSequence_InitType2(type, desc);
}
2010-12-03 16:14:31 -04:00
void
_PyStructSequence_FiniType(PyTypeObject *type)
{
// Ensure that the type is initialized
assert(type->tp_name != NULL);
assert(type->tp_base == &PyTuple_Type);
// Cannot delete a type if it still has subclasses
if (_PyType_HasSubclasses(type)) {
return;
}
// Undo PyStructSequence_NewType()
type->tp_name = NULL;
PyMem_Free(type->tp_members);
_PyStaticType_Dealloc(type);
assert(Py_REFCNT(type) == 1);
// Undo Py_INCREF(type) of _PyStructSequence_InitType().
// Don't use Py_DECREF(): static type must not be deallocated
Py_SET_REFCNT(type, 0);
#ifdef Py_REF_DEBUG
_Py_DecRefTotal(_PyInterpreterState_GET());
#endif
// Make sure that _PyStructSequence_InitType() will initialize
// the type again
assert(Py_REFCNT(type) == 0);
assert(type->tp_name == NULL);
}
PyTypeObject *
_PyStructSequence_NewType(PyStructSequence_Desc *desc, unsigned long tp_flags)
2010-12-03 16:14:31 -04:00
{
PyMemberDef *members;
PyTypeObject *type;
PyType_Slot slots[8];
PyType_Spec spec;
Py_ssize_t n_members, n_unnamed_members;
/* Initialize MemberDefs */
members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
return NULL;
}
/* Initialize Slots */
slots[0] = (PyType_Slot){Py_tp_dealloc, (destructor)structseq_dealloc};
slots[1] = (PyType_Slot){Py_tp_repr, (reprfunc)structseq_repr};
slots[2] = (PyType_Slot){Py_tp_doc, (void *)desc->doc};
slots[3] = (PyType_Slot){Py_tp_methods, structseq_methods};
slots[4] = (PyType_Slot){Py_tp_new, structseq_new};
slots[5] = (PyType_Slot){Py_tp_members, members};
slots[6] = (PyType_Slot){Py_tp_traverse, (traverseproc)structseq_traverse};
slots[7] = (PyType_Slot){0, 0};
/* Initialize Spec */
/* The name in this PyType_Spec is statically allocated so it is */
/* expected that it'll outlive the PyType_Spec */
spec.name = desc->name;
spec.basicsize = sizeof(PyStructSequence) - sizeof(PyObject *);
spec.itemsize = sizeof(PyObject *);
spec.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
spec.slots = slots;
type = (PyTypeObject *)PyType_FromSpecWithBases(&spec, (PyObject *)&PyTuple_Type);
PyMem_Free(members);
if (type == NULL) {
return NULL;
}
if (initialize_structseq_dict(
desc, type->tp_dict, n_members, n_unnamed_members) < 0) {
Py_DECREF(type);
return NULL;
}
return type;
2010-12-03 16:14:31 -04:00
}
PyTypeObject *
PyStructSequence_NewType(PyStructSequence_Desc *desc)
{
return _PyStructSequence_NewType(desc, 0);
}