From 6d6c1a35e08b95a83dbe47dbd9e6474daff00354 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 2 Aug 2001 04:15:00 +0000 Subject: [PATCH] Merge of descr-branch back into trunk. --- Include/Python.h | 1 + Include/abstract.h | 11 + Include/ceval.h | 3 + Include/classobject.h | 4 - Include/descrobject.h | 32 + Include/dictobject.h | 77 +- Include/eval.h | 8 + Include/funcobject.h | 7 + Include/listobject.h | 2 +- Include/modsupport.h | 6 +- Include/object.h | 64 +- Include/objimpl.h | 15 +- Include/patchlevel.h | 6 +- Include/pythonrun.h | 4 +- Lib/pickle.py | 1 + Lib/repr.py | 4 +- Lib/test/test_descr.py | 829 ++++++++++++ Lib/test/test_generators.py | 10 +- Lib/types.py | 12 +- Makefile.pre.in | 2 + Misc/NEWS | 11 + Modules/Setup.dist | 2 + Modules/cPickle.c | 4 + Modules/config.c.in | 2 +- Modules/xxsubtype.c | 233 ++++ Objects/abstract.c | 20 +- Objects/bufferobject.c | 34 +- Objects/cellobject.c | 2 +- Objects/classobject.c | 235 ++-- Objects/complexobject.c | 301 ++++- Objects/descrobject.c | 854 +++++++++++++ Objects/dictobject.c | 230 ++-- Objects/fileobject.c | 45 +- Objects/floatobject.c | 70 +- Objects/frameobject.c | 35 +- Objects/funcobject.c | 380 +++++- Objects/intobject.c | 85 +- Objects/iterobject.c | 34 +- Objects/listobject.c | 158 ++- Objects/longobject.c | 95 +- Objects/methodobject.c | 117 +- Objects/moduleobject.c | 101 +- Objects/object.c | 293 +++-- Objects/rangeobject.c | 44 +- Objects/sliceobject.c | 70 +- Objects/stringobject.c | 76 +- Objects/tupleobject.c | 40 +- Objects/typeobject.c | 2358 ++++++++++++++++++++++++++++++++++- Objects/unicodeobject.c | 54 +- PC/config.c | 3 + PCbuild/pythoncore.dsp | 30 + PLAN.txt | 431 +++++++ Python/bltinmodule.c | 475 +------ Python/ceval.c | 163 +-- Python/exceptions.c | 33 +- Python/import.c | 7 +- Python/pythonrun.c | 9 +- 57 files changed, 6923 insertions(+), 1309 deletions(-) create mode 100644 Include/descrobject.h create mode 100644 Lib/test/test_descr.py create mode 100644 Modules/xxsubtype.c create mode 100644 Objects/descrobject.c create mode 100644 PLAN.txt diff --git a/Include/Python.h b/Include/Python.h index f9c33d42fc0..9757a709143 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -89,6 +89,7 @@ #include "sliceobject.h" #include "cellobject.h" #include "iterobject.h" +#include "descrobject.h" #include "codecs.h" #include "pyerrors.h" diff --git a/Include/abstract.h b/Include/abstract.h index 9082edb0b83..799438e2b1d 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -294,6 +294,17 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ */ + + DL_IMPORT(PyObject *) PyObject_Call(PyObject *callable_object, + PyObject *args, PyObject *kw); + + /* + + Call a callable Python object, callable_object, with + arguments and keywords arguments. The 'args' argument can not be + NULL, but the 'kw' argument can be NULL. + + */ DL_IMPORT(PyObject *) PyObject_CallObject(PyObject *callable_object, PyObject *args); diff --git a/Include/ceval.h b/Include/ceval.h index 0fc5cbabfe0..ae4d8583c6c 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -45,6 +45,9 @@ DL_IMPORT(int) Py_MakePendingCalls(void); DL_IMPORT(void) Py_SetRecursionLimit(int); DL_IMPORT(int) Py_GetRecursionLimit(void); +DL_IMPORT(char *) PyEval_GetFuncName(PyObject *); +DL_IMPORT(char *) PyEval_GetFuncDesc(PyObject *); + /* Interface for threads. A module that plans to do a blocking system call (or something else diff --git a/Include/classobject.h b/Include/classobject.h index 3b25c7447d6..3bd535e1c35 100644 --- a/Include/classobject.h +++ b/Include/classobject.h @@ -47,10 +47,6 @@ extern DL_IMPORT(PyObject *) PyInstance_New(PyObject *, PyObject *, extern DL_IMPORT(PyObject *) PyInstance_NewRaw(PyObject *, PyObject *); extern DL_IMPORT(PyObject *) PyMethod_New(PyObject *, PyObject *, PyObject *); -extern DL_IMPORT(PyObject *) PyMethod_Function(PyObject *); -extern DL_IMPORT(PyObject *) PyMethod_Self(PyObject *); -extern DL_IMPORT(PyObject *) PyMethod_Class(PyObject *); - /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ #define PyMethod_GET_FUNCTION(meth) \ diff --git a/Include/descrobject.h b/Include/descrobject.h new file mode 100644 index 00000000000..b6fc5210223 --- /dev/null +++ b/Include/descrobject.h @@ -0,0 +1,32 @@ +/* XXX getter, setter, getsetlist and wrapperbase need 'Py'-prefixed names */ + +typedef PyObject *(*getter)(PyObject *, void *); +typedef int (*setter)(PyObject *, PyObject *, void *); + +struct getsetlist { + char *name; + getter get; + setter set; + void *closure; +}; + +typedef PyObject *(*wrapperfunc)(PyObject *self, PyObject *args, + void *wrapped); + +struct wrapperbase { + char *name; + wrapperfunc wrapper; + char *doc; +}; + +extern DL_IMPORT(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *); +extern DL_IMPORT(PyObject *) PyDescr_NewMember(PyTypeObject *, + struct memberlist *); +extern DL_IMPORT(PyObject *) PyDescr_NewGetSet(PyTypeObject *, + struct getsetlist *); +extern DL_IMPORT(PyObject *) PyDescr_NewWrapper(PyTypeObject *, + struct wrapperbase *, void *); +extern DL_IMPORT(int) PyDescr_IsData(PyObject *); + +extern DL_IMPORT(PyObject *) PyDictProxy_New(PyObject *); +extern DL_IMPORT(PyObject *) PyWrapper_New(PyObject *, PyObject *); diff --git a/Include/dictobject.h b/Include/dictobject.h index 4f5f94a115c..abc8ed567d0 100644 --- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -7,9 +7,83 @@ extern "C" { /* Dictionary object type -- mapping from hashable object to object */ +/* +There are three kinds of slots in the table: + +1. Unused. me_key == me_value == NULL + Does not hold an active (key, value) pair now and never did. Unused can + transition to Active upon key insertion. This is the only case in which + me_key is NULL, and is each slot's initial state. + +2. Active. me_key != NULL and me_key != dummy and me_value != NULL + Holds an active (key, value) pair. Active can transition to Dummy upon + key deletion. This is the only case in which me_value != NULL. + +3. Dummy. me_key == dummy and me_value == NULL + Previously held an active (key, value) pair, but that was deleted and an + active pair has not yet overwritten the slot. Dummy can transition to + Active upon key insertion. Dummy slots cannot be made Unused again + (cannot have me_key set to NULL), else the probe sequence in case of + collision would have no way to know they were once active. + +Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to +hold a search finger. The me_hash field of Unused or Dummy slots has no +meaning otherwise. +*/ + +/* PyDict_MINSIZE is the minimum size of a dictionary. This many slots are + * allocated directly in the dict object (in the ma_smalltable member). + * It must be a power of 2, and at least 4. 8 allows dicts with no more + * than 5 active entries to live in ma_smalltable (and so avoid an + * additional malloc); instrumentation suggested this suffices for the + * majority of dicts (consisting mostly of usually-small instance dicts and + * usually-small dicts created to pass keyword arguments). + */ +#define PyDict_MINSIZE 8 + +typedef struct { + long me_hash; /* cached hash code of me_key */ + PyObject *me_key; + PyObject *me_value; +#ifdef USE_CACHE_ALIGNED + long aligner; +#endif +} PyDictEntry; + +/* +To ensure the lookup algorithm terminates, there must be at least one Unused +slot (NULL key) in the table. +The value ma_fill is the number of non-NULL keys (sum of Active and Dummy); +ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL +values == the number of Active items). +To avoid slowing down lookups on a near-full table, we resize the table when +it's two-thirds full. +*/ +typedef struct _dictobject PyDictObject; +struct _dictobject { + PyObject_HEAD + int ma_fill; /* # Active + # Dummy */ + int ma_used; /* # Active */ + + /* The table contains ma_mask + 1 slots, and that's a power of 2. + * We store the mask instead of the size because the mask is more + * frequently needed. + */ + int ma_mask; + + /* ma_table points to ma_smalltable for small tables, else to + * additional malloc'ed memory. ma_table is never NULL! This rule + * saves repeated runtime null-tests in the workhorse getitem and + * setitem calls. + */ + PyDictEntry *ma_table; + PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); + PyDictEntry ma_smalltable[PyDict_MINSIZE]; +}; + extern DL_IMPORT(PyTypeObject) PyDict_Type; -#define PyDict_Check(op) ((op)->ob_type == &PyDict_Type) +#define PyDict_Check(op) PyObject_TypeCheck(op, &PyDict_Type) extern DL_IMPORT(PyObject *) PyDict_New(void); extern DL_IMPORT(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key); @@ -23,6 +97,7 @@ extern DL_IMPORT(PyObject *) PyDict_Values(PyObject *mp); extern DL_IMPORT(PyObject *) PyDict_Items(PyObject *mp); extern DL_IMPORT(int) PyDict_Size(PyObject *mp); extern DL_IMPORT(PyObject *) PyDict_Copy(PyObject *mp); +extern DL_IMPORT(int) PyDict_Update(PyObject *mp, PyObject *other); extern DL_IMPORT(PyObject *) PyDict_GetItemString(PyObject *dp, char *key); diff --git a/Include/eval.h b/Include/eval.h index 2a450090257..f9334a75f07 100644 --- a/Include/eval.h +++ b/Include/eval.h @@ -9,6 +9,14 @@ extern "C" { DL_IMPORT(PyObject *) PyEval_EvalCode(PyCodeObject *, PyObject *, PyObject *); +DL_IMPORT(PyObject *) PyEval_EvalCodeEx(PyCodeObject *co, + PyObject *globals, + PyObject *locals, + PyObject **args, int argc, + PyObject **kwds, int kwdc, + PyObject **defs, int defc, + PyObject *closure); + #ifdef __cplusplus } #endif diff --git a/Include/funcobject.h b/Include/funcobject.h index 8cedeb7efdc..6b1e389b7ce 100644 --- a/Include/funcobject.h +++ b/Include/funcobject.h @@ -42,6 +42,13 @@ extern DL_IMPORT(int) PyFunction_SetClosure(PyObject *, PyObject *); #define PyFunction_GET_CLOSURE(func) \ (((PyFunctionObject *)func) -> func_closure) +/* The classmethod and staticmethod types lives here, too */ +extern DL_IMPORT(PyTypeObject) PyClassMethod_Type; +extern DL_IMPORT(PyTypeObject) PyStaticMethod_Type; + +extern DL_IMPORT(PyObject *) PyClassMethod_New(PyObject *); +extern DL_IMPORT(PyObject *) PyStaticMethod_New(PyObject *); + #ifdef __cplusplus } #endif diff --git a/Include/listobject.h b/Include/listobject.h index 73ac724e08f..af1368c987b 100644 --- a/Include/listobject.h +++ b/Include/listobject.h @@ -26,7 +26,7 @@ typedef struct { extern DL_IMPORT(PyTypeObject) PyList_Type; -#define PyList_Check(op) ((op)->ob_type == &PyList_Type) +#define PyList_Check(op) PyObject_TypeCheck(op, &PyList_Type) extern DL_IMPORT(PyObject *) PyList_New(int size); extern DL_IMPORT(int) PyList_Size(PyObject *); diff --git a/Include/modsupport.h b/Include/modsupport.h index 4a40499eb56..8ef343c6d75 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -22,8 +22,8 @@ extern DL_IMPORT(int) PyModule_AddObject(PyObject *, char *, PyObject *); extern DL_IMPORT(int) PyModule_AddIntConstant(PyObject *, char *, long); extern DL_IMPORT(int) PyModule_AddStringConstant(PyObject *, char *, char *); -#define PYTHON_API_VERSION 1010 -#define PYTHON_API_STRING "1010" +#define PYTHON_API_VERSION 1011 +#define PYTHON_API_STRING "1011" /* The API version is maintained (independently from the Python version) so we can detect mismatches between the interpreter and dynamically loaded modules. These are diagnosed by an error message but @@ -37,6 +37,8 @@ extern DL_IMPORT(int) PyModule_AddStringConstant(PyObject *, char *, char *); Please add a line or two to the top of this log for each API version change: + 17-Jul-2001 GvR 1011 Descr-branch, just to be on the safe side + 25-Jan-2001 FLD 1010 Parameters added to PyCode_New() and PyFrame_New(); Python 2.1a2 diff --git a/Include/object.h b/Include/object.h index 0765748e9fc..d81d4c20a29 100644 --- a/Include/object.h +++ b/Include/object.h @@ -202,6 +202,11 @@ typedef long (*hashfunc)(PyObject *); typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int); typedef PyObject *(*getiterfunc) (PyObject *); typedef PyObject *(*iternextfunc) (PyObject *); +typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*initproc)(PyObject *, PyObject *, PyObject *); +typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *); +typedef PyObject *(*allocfunc)(struct _typeobject *, int); typedef struct _typeobject { PyObject_VAR_HEAD @@ -255,18 +260,48 @@ typedef struct _typeobject { getiterfunc tp_iter; iternextfunc tp_iternext; + /* Attribute descriptor and subclassing stuff */ + struct PyMethodDef *tp_methods; + struct memberlist *tp_members; + struct getsetlist *tp_getset; + struct _typeobject *tp_base; + PyObject *tp_dict; + descrgetfunc tp_descr_get; + descrsetfunc tp_descr_set; + long tp_dictoffset; + initproc tp_init; + allocfunc tp_alloc; + newfunc tp_new; + destructor tp_free; /* Low-level free-memory routine */ + PyObject *tp_bases; + PyObject *tp_mro; /* method resolution order */ + PyObject *tp_defined; + #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ - int tp_alloc; - int tp_free; + int tp_allocs; + int tp_frees; int tp_maxalloc; struct _typeobject *tp_next; #endif } PyTypeObject; -extern DL_IMPORT(PyTypeObject) PyType_Type; /* The type of type objects */ -#define PyType_Check(op) ((op)->ob_type == &PyType_Type) +/* Generic type check */ +extern DL_IMPORT(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *); +#define PyObject_TypeCheck(ob, tp) \ + ((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp))) + +extern DL_IMPORT(PyTypeObject) PyType_Type; /* Metatype */ +extern DL_IMPORT(PyTypeObject) PyBaseObject_Type; /* Most base object type */ + +#define PyType_Check(op) PyObject_TypeCheck(op, &PyType_Type) + +extern DL_IMPORT(int) PyType_InitDict(PyTypeObject *); +extern DL_IMPORT(PyObject *) PyType_GenericAlloc(PyTypeObject *, int); +extern DL_IMPORT(PyObject *) PyType_GenericNew(PyTypeObject *, + PyObject *, PyObject *); +extern DL_IMPORT(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); /* Generic operations on objects */ extern DL_IMPORT(int) PyObject_Print(PyObject *, FILE *, int); @@ -283,6 +318,10 @@ extern DL_IMPORT(int) PyObject_HasAttrString(PyObject *, char *); extern DL_IMPORT(PyObject *) PyObject_GetAttr(PyObject *, PyObject *); extern DL_IMPORT(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); extern DL_IMPORT(int) PyObject_HasAttr(PyObject *, PyObject *); +extern DL_IMPORT(PyObject **) _PyObject_GetDictPtr(PyObject *); +extern DL_IMPORT(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); +extern DL_IMPORT(int) PyObject_GenericSetAttr(PyObject *, + PyObject *, PyObject *); extern DL_IMPORT(long) PyObject_Hash(PyObject *); extern DL_IMPORT(int) PyObject_IsTrue(PyObject *); extern DL_IMPORT(int) PyObject_Not(PyObject *); @@ -357,6 +396,18 @@ given type object has a specified feature. /* tp_iter is defined */ #define Py_TPFLAGS_HAVE_ITER (1L<<7) +/* Experimental stuff for healing the type/class split */ +#define Py_TPFLAGS_HAVE_CLASS (1L<<8) + +/* Set if the type object is dynamically allocated */ +#define Py_TPFLAGS_HEAPTYPE (1L<<9) + +/* Set if the type allows subclassing */ +#define Py_TPFLAGS_BASETYPE (1L<<10) + +/* Set if the type's __dict__ may change */ +#define Py_TPFLAGS_DYNAMICTYPE (1L<<11) + #define Py_TPFLAGS_DEFAULT ( \ Py_TPFLAGS_HAVE_GETCHARBUFFER | \ Py_TPFLAGS_HAVE_SEQUENCE_IN | \ @@ -364,6 +415,7 @@ given type object has a specified feature. Py_TPFLAGS_HAVE_RICHCOMPARE | \ Py_TPFLAGS_HAVE_WEAKREFS | \ Py_TPFLAGS_HAVE_ITER | \ + Py_TPFLAGS_HAVE_CLASS | \ 0) #define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0) @@ -412,8 +464,8 @@ extern DL_IMPORT(void) _Py_ResetReferences(void); #ifndef Py_TRACE_REFS #ifdef COUNT_ALLOCS -#define _Py_Dealloc(op) ((op)->ob_type->tp_free++, (*(op)->ob_type->tp_dealloc)((PyObject *)(op))) -#define _Py_ForgetReference(op) ((op)->ob_type->tp_free++) +#define _Py_Dealloc(op) ((op)->ob_type->tp_frees++, (*(op)->ob_type->tp_dealloc)((PyObject *)(op))) +#define _Py_ForgetReference(op) ((op)->ob_type->tp_frees++) #else /* !COUNT_ALLOCS */ #define _Py_Dealloc(op) (*(op)->ob_type->tp_dealloc)((PyObject *)(op)) #define _Py_ForgetReference(op) /*empty*/ diff --git a/Include/objimpl.h b/Include/objimpl.h index 4aa38d5673d..18cece8e251 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -236,7 +236,13 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *); #define PyObject_GC_Fini(op) #define PyObject_AS_GC(op) (op) #define PyObject_FROM_GC(op) (op) - +#define PyType_IS_GC(t) 0 +#define PyObject_IS_GC(o) 0 +#define PyObject_AS_GC(o) (o) +#define PyObject_FROM_GC(o) (o) +#define PyType_BASICSIZE(t) ((t)->tp_basicsize) +#define PyType_SET_BASICSIZE(t, s) ((t)->tp_basicsize = (s)) + #else /* Add the object into the container set */ @@ -269,6 +275,13 @@ typedef struct _gc_head { /* Get the object given the PyGC_Head */ #define PyObject_FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1)) +/* Calculate tp_basicsize excluding PyGC_HEAD_SIZE if applicable */ +#define PyType_BASICSIZE(t) (!PyType_IS_GC(t) ? (t)->tp_basicsize : \ + (t)->tp_basicsize - PyGC_HEAD_SIZE) +#define PyType_SET_BASICSIZE(t, s) (!PyType_IS_GC(t) ? \ + ((t)->tp_basicsize = (s)) : \ + ((t)->tp_basicsize = (s) + PyGC_HEAD_SIZE)) + extern DL_IMPORT(void) _PyGC_Dump(PyGC_Head *); #endif /* WITH_CYCLE_GC */ diff --git a/Include/patchlevel.h b/Include/patchlevel.h index dddc28f017a..85ffacb2b00 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,13 +23,13 @@ #define PY_MINOR_VERSION 2 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 0 +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "2.2a0" +#define PY_VERSION "2.2a1" /* Historic */ -#define PATCHLEVEL "2.2a0" +#define PATCHLEVEL "2.2a1" /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ diff --git a/Include/pythonrun.h b/Include/pythonrun.h index 6e9821bf7fc..7d947dffe4b 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -92,10 +92,10 @@ DL_IMPORT(const char *) Py_GetBuildInfo(void); DL_IMPORT(PyObject *) _PyBuiltin_Init(void); DL_IMPORT(PyObject *) _PySys_Init(void); DL_IMPORT(void) _PyImport_Init(void); -DL_IMPORT(void) init_exceptions(void); +DL_IMPORT(void) _PyExc_Init(void); /* Various internal finalizers */ -DL_IMPORT(void) fini_exceptions(void); +DL_IMPORT(void) _PyExc_Fini(void); DL_IMPORT(void) _PyImport_Fini(void); DL_IMPORT(void) PyMethod_Fini(void); DL_IMPORT(void) PyFrame_Fini(void); diff --git a/Lib/pickle.py b/Lib/pickle.py index f6cbea8f182..c92dac276c6 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -504,6 +504,7 @@ class Pickler: dispatch[ClassType] = save_global dispatch[FunctionType] = save_global dispatch[BuiltinFunctionType] = save_global + dispatch[TypeType] = save_global def _keep_alive(x, memo): diff --git a/Lib/repr.py b/Lib/repr.py index c2d3ed59f4e..f9cfcf647aa 100644 --- a/Lib/repr.py +++ b/Lib/repr.py @@ -62,7 +62,7 @@ class Repr: s = s + ': ' + self.repr1(x[key], level-1) if n > self.maxdict: s = s + ', ...' return '{' + s + '}' - def repr_string(self, x, level): + def repr_str(self, x, level): s = `x[:self.maxstring]` if len(s) > self.maxstring: i = max(0, (self.maxstring-3)/2) @@ -70,7 +70,7 @@ class Repr: s = `x[:i] + x[len(x)-j:]` s = s[:i] + '...' + s[len(s)-j:] return s - def repr_long_int(self, x, level): + def repr_long(self, x, level): s = `x` # XXX Hope this isn't too slow... if len(s) > self.maxlong: i = max(0, (self.maxlong-3)/2) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py new file mode 100644 index 00000000000..92f79d55985 --- /dev/null +++ b/Lib/test/test_descr.py @@ -0,0 +1,829 @@ +# Test descriptor-related enhancements + +from test_support import verify, verbose +from copy import deepcopy + +def testunop(a, res, expr="len(a)", meth="__len__"): + if verbose: print "checking", expr + dict = {'a': a} + verify(eval(expr, dict) == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + verify(m(a) == res) + bm = getattr(a, meth) + verify(bm() == res) + +def testbinop(a, b, res, expr="a+b", meth="__add__"): + if verbose: print "checking", expr + dict = {'a': a, 'b': b} + verify(eval(expr, dict) == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + verify(m(a, b) == res) + bm = getattr(a, meth) + verify(bm(b) == res) + +def testternop(a, b, c, res, expr="a[b:c]", meth="__getslice__"): + if verbose: print "checking", expr + dict = {'a': a, 'b': b, 'c': c} + verify(eval(expr, dict) == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + verify(m(a, b, c) == res) + bm = getattr(a, meth) + verify(bm(b, c) == res) + +def testsetop(a, b, res, stmt="a+=b", meth="__iadd__"): + if verbose: print "checking", stmt + dict = {'a': deepcopy(a), 'b': b} + exec stmt in dict + verify(dict['a'] == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + dict['a'] = deepcopy(a) + m(dict['a'], b) + verify(dict['a'] == res) + dict['a'] = deepcopy(a) + bm = getattr(dict['a'], meth) + bm(b) + verify(dict['a'] == res) + +def testset2op(a, b, c, res, stmt="a[b]=c", meth="__setitem__"): + if verbose: print "checking", stmt + dict = {'a': deepcopy(a), 'b': b, 'c': c} + exec stmt in dict + verify(dict['a'] == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + dict['a'] = deepcopy(a) + m(dict['a'], b, c) + verify(dict['a'] == res) + dict['a'] = deepcopy(a) + bm = getattr(dict['a'], meth) + bm(b, c) + verify(dict['a'] == res) + +def testset3op(a, b, c, d, res, stmt="a[b:c]=d", meth="__setslice__"): + if verbose: print "checking", stmt + dict = {'a': deepcopy(a), 'b': b, 'c': c, 'd': d} + exec stmt in dict + verify(dict['a'] == res) + t = type(a) + m = getattr(t, meth) + verify(m == t.__dict__[meth]) + dict['a'] = deepcopy(a) + m(dict['a'], b, c, d) + verify(dict['a'] == res) + dict['a'] = deepcopy(a) + bm = getattr(dict['a'], meth) + bm(b, c, d) + verify(dict['a'] == res) + +def lists(): + if verbose: print "Testing list operations..." + testbinop([1], [2], [1,2], "a+b", "__add__") + testbinop([1,2,3], 2, 1, "b in a", "__contains__") + testbinop([1,2,3], 4, 0, "b in a", "__contains__") + testbinop([1,2,3], 1, 2, "a[b]", "__getitem__") + testternop([1,2,3], 0, 2, [1,2], "a[b:c]", "__getslice__") + testsetop([1], [2], [1,2], "a+=b", "__iadd__") + testsetop([1,2], 3, [1,2,1,2,1,2], "a*=b", "__imul__") + testunop([1,2,3], 3, "len(a)", "__len__") + testbinop([1,2], 3, [1,2,1,2,1,2], "a*b", "__mul__") + testbinop([1,2], 3, [1,2,1,2,1,2], "b*a", "__rmul__") + testset2op([1,2], 1, 3, [1,3], "a[b]=c", "__setitem__") + testset3op([1,2,3,4], 1, 3, [5,6], [1,5,6,4], "a[b:c]=d", "__setslice__") + +def dicts(): + if verbose: print "Testing dict operations..." + testbinop({1:2}, {2:1}, -1, "cmp(a,b)", "__cmp__") + testbinop({1:2,3:4}, 1, 1, "b in a", "__contains__") + testbinop({1:2,3:4}, 2, 0, "b in a", "__contains__") + testbinop({1:2,3:4}, 1, 2, "a[b]", "__getitem__") + d = {1:2,3:4} + l1 = [] + for i in d.keys(): l1.append(i) + l = [] + for i in iter(d): l.append(i) + verify(l == l1) + l = [] + for i in d.__iter__(): l.append(i) + verify(l == l1) + l = [] + for i in dictionary.__iter__(d): l.append(i) + verify(l == l1) + d = {1:2, 3:4} + testunop(d, 2, "len(a)", "__len__") + verify(eval(repr(d), {}) == d) + verify(eval(d.__repr__(), {}) == d) + testset2op({1:2,3:4}, 2, 3, {1:2,2:3,3:4}, "a[b]=c", "__setitem__") + +binops = { + 'add': '+', + 'sub': '-', + 'mul': '*', + 'div': '/', + 'mod': '%', + 'divmod': 'divmod', + 'pow': '**', + 'lshift': '<<', + 'rshift': '>>', + 'and': '&', + 'xor': '^', + 'or': '|', + 'cmp': 'cmp', + 'lt': '<', + 'le': '<=', + 'eq': '==', + 'ne': '!=', + 'gt': '>', + 'ge': '>=', + } + +for name, expr in binops.items(): + if expr.islower(): + expr = expr + "(a, b)" + else: + expr = 'a %s b' % expr + binops[name] = expr + +unops = { + 'pos': '+', + 'neg': '-', + 'abs': 'abs', + 'invert': '~', + 'int': 'int', + 'long': 'long', + 'float': 'float', + 'oct': 'oct', + 'hex': 'hex', + } + +for name, expr in unops.items(): + if expr.islower(): + expr = expr + "(a)" + else: + expr = '%s a' % expr + unops[name] = expr + +def numops(a, b, skip=[]): + dict = {'a': a, 'b': b} + for name, expr in binops.items(): + if name not in skip: + name = "__%s__" % name + if hasattr(a, name): + res = eval(expr, dict) + testbinop(a, b, res, expr, name) + for name, expr in unops.items(): + name = "__%s__" % name + if hasattr(a, name): + res = eval(expr, dict) + testunop(a, res, expr, name) + +def ints(): + if verbose: print "Testing int operations..." + numops(100, 3) + +def longs(): + if verbose: print "Testing long operations..." + numops(100L, 3L) + +def floats(): + if verbose: print "Testing float operations..." + numops(100.0, 3.0) + +def complexes(): + if verbose: print "Testing complex operations..." + numops(100.0j, 3.0j, skip=['lt', 'le', 'gt', 'ge']) + class Number(complex): + __slots__ = ['prec'] + def __init__(self, *args, **kwds): + self.prec = kwds.get('prec', 12) + def __repr__(self): + prec = self.prec + if self.imag == 0.0: + return "%.*g" % (prec, self.real) + if self.real == 0.0: + return "%.*gj" % (prec, self.imag) + return "(%.*g+%.*gj)" % (prec, self.real, prec, self.imag) + __str__ = __repr__ + a = Number(3.14, prec=6) + verify(`a` == "3.14") + verify(a.prec == 6) + +def spamlists(): + if verbose: print "Testing spamlist operations..." + import copy, xxsubtype as spam + def spamlist(l, memo=None): + import xxsubtype as spam + return spam.spamlist(l) + # This is an ugly hack: + copy._deepcopy_dispatch[spam.spamlist] = spamlist + + testbinop(spamlist([1]), spamlist([2]), spamlist([1,2]), "a+b", "__add__") + testbinop(spamlist([1,2,3]), 2, 1, "b in a", "__contains__") + testbinop(spamlist([1,2,3]), 4, 0, "b in a", "__contains__") + testbinop(spamlist([1,2,3]), 1, 2, "a[b]", "__getitem__") + testternop(spamlist([1,2,3]), 0, 2, spamlist([1,2]), + "a[b:c]", "__getslice__") + testsetop(spamlist([1]), spamlist([2]), spamlist([1,2]), + "a+=b", "__iadd__") + testsetop(spamlist([1,2]), 3, spamlist([1,2,1,2,1,2]), "a*=b", "__imul__") + testunop(spamlist([1,2,3]), 3, "len(a)", "__len__") + testbinop(spamlist([1,2]), 3, spamlist([1,2,1,2,1,2]), "a*b", "__mul__") + testbinop(spamlist([1,2]), 3, spamlist([1,2,1,2,1,2]), "b*a", "__rmul__") + testset2op(spamlist([1,2]), 1, 3, spamlist([1,3]), "a[b]=c", "__setitem__") + testset3op(spamlist([1,2,3,4]), 1, 3, spamlist([5,6]), + spamlist([1,5,6,4]), "a[b:c]=d", "__setslice__") + # Test subclassing + class C(spam.spamlist): + def foo(self): return 1 + a = C() + verify(a == []) + verify(a.foo() == 1) + a.append(100) + verify(a == [100]) + verify(a.getstate() == 0) + a.setstate(42) + verify(a.getstate() == 42) + +def spamdicts(): + if verbose: print "Testing spamdict operations..." + import copy, xxsubtype as spam + def spamdict(d, memo=None): + import xxsubtype as spam + sd = spam.spamdict() + for k, v in d.items(): sd[k] = v + return sd + # This is an ugly hack: + copy._deepcopy_dispatch[spam.spamdict] = spamdict + + testbinop(spamdict({1:2}), spamdict({2:1}), -1, "cmp(a,b)", "__cmp__") + testbinop(spamdict({1:2,3:4}), 1, 1, "b in a", "__contains__") + testbinop(spamdict({1:2,3:4}), 2, 0, "b in a", "__contains__") + testbinop(spamdict({1:2,3:4}), 1, 2, "a[b]", "__getitem__") + d = spamdict({1:2,3:4}) + l1 = [] + for i in d.keys(): l1.append(i) + l = [] + for i in iter(d): l.append(i) + verify(l == l1) + l = [] + for i in d.__iter__(): l.append(i) + verify(l == l1) + l = [] + for i in type(spamdict({})).__iter__(d): l.append(i) + verify(l == l1) + straightd = {1:2, 3:4} + spamd = spamdict(straightd) + testunop(spamd, 2, "len(a)", "__len__") + testunop(spamd, repr(straightd), "repr(a)", "__repr__") + testset2op(spamdict({1:2,3:4}), 2, 3, spamdict({1:2,2:3,3:4}), + "a[b]=c", "__setitem__") + # Test subclassing + class C(spam.spamdict): + def foo(self): return 1 + a = C() + verify(a.items() == []) + verify(a.foo() == 1) + a['foo'] = 'bar' + verify(a.items() == [('foo', 'bar')]) + verify(a.getstate() == 0) + a.setstate(100) + verify(a.getstate() == 100) + +def pydicts(): + if verbose: print "Testing Python subclass of dict..." + verify(issubclass(dictionary, dictionary)) + verify(isinstance({}, dictionary)) + d = dictionary() + verify(d == {}) + verify(d.__class__ is dictionary) + verify(isinstance(d, dictionary)) + class C(dictionary): + state = -1 + def __init__(self, *a, **kw): + if a: + assert len(a) == 1 + self.state = a[0] + if kw: + for k, v in kw.items(): self[v] = k + def __getitem__(self, key): + return self.get(key, 0) + def __setitem__(self, key, value): + assert isinstance(key, type(0)) + dictionary.__setitem__(self, key, value) + def setstate(self, state): + self.state = state + def getstate(self): + return self.state + verify(issubclass(C, dictionary)) + a1 = C(12) + verify(a1.state == 12) + a2 = C(foo=1, bar=2) + verify(a2[1] == 'foo' and a2[2] == 'bar') + a = C() + verify(a.state == -1) + verify(a.getstate() == -1) + a.setstate(0) + verify(a.state == 0) + verify(a.getstate() == 0) + a.setstate(10) + verify(a.state == 10) + verify(a.getstate() == 10) + verify(a[42] == 0) + a[42] = 24 + verify(a[42] == 24) + if verbose: print "pydict stress test ..." + N = 50 + for i in range(N): + a[i] = C() + for j in range(N): + a[i][j] = i*j + for i in range(N): + for j in range(N): + verify(a[i][j] == i*j) + +def pylists(): + if verbose: print "Testing Python subclass of list..." + class C(list): + def __getitem__(self, i): + return list.__getitem__(self, i) + 100 + def __getslice__(self, i, j): + return (i, j) + a = C() + a.extend([0,1,2]) + verify(a[0] == 100) + verify(a[1] == 101) + verify(a[2] == 102) + verify(a[100:200] == (100,200)) + +def metaclass(): + if verbose: print "Testing __metaclass__..." + global C + class C: + __metaclass__ = type + def __init__(self): + self.__state = 0 + def getstate(self): + return self.__state + def setstate(self, state): + self.__state = state + a = C() + verify(a.getstate() == 0) + a.setstate(10) + verify(a.getstate() == 10) + class D: + class __metaclass__(type): + def myself(cls): return cls + verify(D.myself() == D) + +import sys +MT = type(sys) + +def pymods(): + if verbose: print "Testing Python subclass of module..." + global log + log = [] + class MM(MT): + def __init__(self): + MT.__init__(self) + def __getattr__(self, name): + log.append(("getattr", name)) + return MT.__getattr__(self, name) + def __setattr__(self, name, value): + log.append(("setattr", name, value)) + MT.__setattr__(self, name, value) + def __delattr__(self, name): + log.append(("delattr", name)) + MT.__delattr__(self, name) + a = MM() + a.foo = 12 + x = a.foo + del a.foo + verify(log == [('getattr', '__init__'), + ('getattr', '__setattr__'), + ("setattr", "foo", 12), + ("getattr", "foo"), + ('getattr', '__delattr__'), + ("delattr", "foo")], log) + +def multi(): + if verbose: print "Testing multiple inheritance..." + global C + class C(object): + def __init__(self): + self.__state = 0 + def getstate(self): + return self.__state + def setstate(self, state): + self.__state = state + a = C() + verify(a.getstate() == 0) + a.setstate(10) + verify(a.getstate() == 10) + class D(dictionary, C): + def __init__(self): + type({}).__init__(self) + C.__init__(self) + d = D() + verify(d.keys() == []) + d["hello"] = "world" + verify(d.items() == [("hello", "world")]) + verify(d["hello"] == "world") + verify(d.getstate() == 0) + d.setstate(10) + verify(d.getstate() == 10) + verify(D.__mro__ == (D, dictionary, C, object)) + +def diamond(): + if verbose: print "Testing multiple inheritance special cases..." + class A(object): + def spam(self): return "A" + verify(A().spam() == "A") + class B(A): + def boo(self): return "B" + def spam(self): return "B" + verify(B().spam() == "B") + verify(B().boo() == "B") + class C(A): + def boo(self): return "C" + verify(C().spam() == "A") + verify(C().boo() == "C") + class D(B, C): pass + verify(D().spam() == "B") + verify(D().boo() == "B") + verify(D.__mro__ == (D, B, C, A, object)) + class E(C, B): pass + verify(E().spam() == "B") + verify(E().boo() == "C") + verify(E.__mro__ == (E, C, B, A, object)) + class F(D, E): pass + verify(F().spam() == "B") + verify(F().boo() == "B") + verify(F.__mro__ == (F, D, E, B, C, A, object)) + class G(E, D): pass + verify(G().spam() == "B") + verify(G().boo() == "C") + verify(G.__mro__ == (G, E, D, C, B, A, object)) + +def objects(): + if verbose: print "Testing object class..." + a = object() + verify(a.__class__ == object == type(a)) + b = object() + verify(a is not b) + verify(not hasattr(a, "foo")) + try: + a.foo = 12 + except TypeError: + pass + else: + verify(0, "object() should not allow setting a foo attribute") + verify(not hasattr(object(), "__dict__")) + + class Cdict(object): + pass + x = Cdict() + verify(x.__dict__ is None) + x.foo = 1 + verify(x.foo == 1) + verify(x.__dict__ == {'foo': 1}) + +def slots(): + if verbose: print "Testing __slots__..." + class C0(object): + __slots__ = [] + x = C0() + verify(not hasattr(x, "__dict__")) + verify(not hasattr(x, "foo")) + + class C1(object): + __slots__ = ['a'] + x = C1() + verify(not hasattr(x, "__dict__")) + verify(x.a == None) + x.a = 1 + verify(x.a == 1) + del x.a + verify(x.a == None) + + class C3(object): + __slots__ = ['a', 'b', 'c'] + x = C3() + verify(not hasattr(x, "__dict__")) + verify(x.a is None) + verify(x.b is None) + verify(x.c is None) + x.a = 1 + x.b = 2 + x.c = 3 + verify(x.a == 1) + verify(x.b == 2) + verify(x.c == 3) + +def dynamics(): + if verbose: print "Testing __dynamic__..." + verify(object.__dynamic__ == 0) + verify(list.__dynamic__ == 0) + class S1: + __metaclass__ = type + verify(S1.__dynamic__ == 0) + class S(object): + pass + verify(C.__dynamic__ == 0) + class D(object): + __dynamic__ = 1 + verify(D.__dynamic__ == 1) + class E(D, S): + pass + verify(E.__dynamic__ == 1) + class F(S, D): + pass + verify(F.__dynamic__ == 1) + try: + S.foo = 1 + except (AttributeError, TypeError): + pass + else: + verify(0, "assignment to a static class attribute should be illegal") + D.foo = 1 + verify(D.foo == 1) + # Test that dynamic attributes are inherited + verify(E.foo == 1) + verify(F.foo == 1) + class SS(D): + __dynamic__ = 0 + verify(SS.__dynamic__ == 0) + verify(SS.foo == 1) + try: + SS.foo = 1 + except (AttributeError, TypeError): + pass + else: + verify(0, "assignment to SS.foo should be illegal") + +def errors(): + if verbose: print "Testing errors..." + + try: + class C(list, dictionary): + pass + except TypeError: + pass + else: + verify(0, "inheritance from both list and dict should be illegal") + + try: + class C(object, None): + pass + except TypeError: + pass + else: + verify(0, "inheritance from non-type should be illegal") + class Classic: + pass + + try: + class C(object, Classic): + pass + except TypeError: + pass + else: + verify(0, "inheritance from object and Classic should be illegal") + + try: + class C(int): + pass + except TypeError: + pass + else: + verify(0, "inheritance from int should be illegal") + + try: + class C(object): + __slots__ = 1 + except TypeError: + pass + else: + verify(0, "__slots__ = 1 should be illegal") + + try: + class C(object): + __slots__ = [1] + except TypeError: + pass + else: + verify(0, "__slots__ = [1] should be illegal") + +def classmethods(): + if verbose: print "Testing class methods..." + class C(object): + def foo(*a): return a + goo = classmethod(foo) + c = C() + verify(C.goo(1) == (C, 1)) + verify(c.goo(1) == (C, 1)) + verify(c.foo(1) == (c, 1)) + class D(C): + pass + d = D() + verify(D.goo(1) == (D, 1)) + verify(d.goo(1) == (D, 1)) + verify(d.foo(1) == (d, 1)) + verify(D.foo(d, 1) == (d, 1)) + +def staticmethods(): + if verbose: print "Testing static methods..." + class C(object): + def foo(*a): return a + goo = staticmethod(foo) + c = C() + verify(C.goo(1) == (1,)) + verify(c.goo(1) == (1,)) + verify(c.foo(1) == (c, 1,)) + class D(C): + pass + d = D() + verify(D.goo(1) == (1,)) + verify(d.goo(1) == (1,)) + verify(d.foo(1) == (d, 1)) + verify(D.foo(d, 1) == (d, 1)) + +def classic(): + if verbose: print "Testing classic classes..." + class C: + def foo(*a): return a + goo = classmethod(foo) + c = C() + verify(C.goo(1) == (C, 1)) + verify(c.goo(1) == (C, 1)) + verify(c.foo(1) == (c, 1)) + class D(C): + pass + d = D() + verify(D.goo(1) == (D, 1)) + verify(d.goo(1) == (D, 1)) + verify(d.foo(1) == (d, 1)) + verify(D.foo(d, 1) == (d, 1)) + +def compattr(): + if verbose: print "Testing computed attributes..." + class C(object): + class computed_attribute(object): + def __init__(self, get, set=None): + self.__get = get + self.__set = set + def __get__(self, obj, type=None): + return self.__get(obj) + def __set__(self, obj, value): + return self.__set(obj, value) + def __init__(self): + self.__x = 0 + def __get_x(self): + x = self.__x + self.__x = x+1 + return x + def __set_x(self, x): + self.__x = x + x = computed_attribute(__get_x, __set_x) + a = C() + verify(a.x == 0) + verify(a.x == 1) + a.x = 10 + verify(a.x == 10) + verify(a.x == 11) + +def newslot(): + if verbose: print "Testing __new__ slot override..." + class C(list): + def __new__(cls): + self = list.__new__(cls) + self.foo = 1 + return self + def __init__(self): + self.foo = self.foo + 2 + a = C() + verify(a.foo == 3) + verify(a.__class__ is C) + class D(C): + pass + b = D() + verify(b.foo == 3) + verify(b.__class__ is D) + +class PerverseMetaType(type): + def mro(cls): + L = type.mro(cls) + L.reverse() + return L + +def altmro(): + if verbose: print "Testing mro() and overriding it..." + class A(object): + def f(self): return "A" + class B(A): + pass + class C(A): + def f(self): return "C" + class D(B, C): + pass + verify(D.mro() == [D, B, C, A, object] == list(D.__mro__)) + verify(D().f() == "C") + class X(A,B,C,D): + __metaclass__ = PerverseMetaType + verify(X.__mro__ == (object, A, C, B, D, X)) + verify(X().f() == "A") + +def overloading(): + if verbose: print "testing operator overloading..." + + class B(object): + "Intermediate class because object doesn't have a __setattr__" + + class C(B): + + def __getattr__(self, name): + if name == "foo": + return ("getattr", name) + else: + return B.__getattr__(self, name) + def __setattr__(self, name, value): + if name == "foo": + self.setattr = (name, value) + else: + return B.__setattr__(self, name, value) + def __delattr__(self, name): + if name == "foo": + self.delattr = name + else: + return B.__delattr__(self, name) + + def __getitem__(self, key): + return ("getitem", key) + def __setitem__(self, key, value): + self.setitem = (key, value) + def __delitem__(self, key): + self.delitem = key + + def __getslice__(self, i, j): + return ("getslice", i, j) + def __setslice__(self, i, j, value): + self.setslice = (i, j, value) + def __delslice__(self, i, j): + self.delslice = (i, j) + + a = C() + verify(a.foo == ("getattr", "foo")) + a.foo = 12 + verify(a.setattr == ("foo", 12)) + del a.foo + verify(a.delattr == "foo") + + verify(a[12] == ("getitem", 12)) + a[12] = 21 + verify(a.setitem == (12, 21)) + del a[12] + verify(a.delitem == 12) + + verify(a[0:10] == ("getslice", 0, 10)) + a[0:10] = "foo" + verify(a.setslice == (0, 10, "foo")) + del a[0:10] + verify(a.delslice == (0, 10)) + +def all(): + lists() + dicts() + ints() + longs() + floats() + complexes() + spamlists() + spamdicts() + pydicts() + pylists() + metaclass() + pymods() + multi() + diamond() + objects() + slots() + dynamics() + errors() + classmethods() + staticmethods() + classic() + compattr() + newslot() + altmro() + overloading() + +all() + +if verbose: print "All OK" diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 2a174c3d811..72a70ec4713 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -380,10 +380,16 @@ From the Iterators list, about the types of these things. >>> i = g() >>> type(i) + +XXX dir(object) *generally* doesn't return useful stuff in descr-branch. >>> dir(i) +[] + +Was hoping to see this instead: ['gi_frame', 'gi_running', 'next'] + >>> print i.next.__doc__ -next() -- get the next value, or raise StopIteration +x.next() -> the next value, or raise StopIteration >>> iter(i) is i 1 >>> import types @@ -399,7 +405,7 @@ And more, added later. >>> i.gi_running = 42 Traceback (most recent call last): ... -TypeError: object has read-only attributes +TypeError: 'generator' object has only read-only attributes (assign to .gi_running) >>> def g(): ... yield me.gi_running >>> me = g() diff --git a/Lib/types.py b/Lib/types.py index 95600a3b9aa..d60ee56d7e8 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -7,7 +7,8 @@ from __future__ import generators import sys NoneType = type(None) -TypeType = type(NoneType) +TypeType = type +ObjectType = object IntType = type(0) LongType = type(0L) @@ -22,8 +23,8 @@ UnicodeType = type(u'') BufferType = type(buffer('')) TupleType = type(()) -ListType = type([]) -DictType = DictionaryType = type({}) +ListType = list +DictType = DictionaryType = dictionary def _f(): pass FunctionType = type(_f) @@ -71,4 +72,9 @@ except TypeError: SliceType = type(slice(0)) EllipsisType = type(Ellipsis) +DictIterType = type(iter({})) +SequenceIterType = type(iter([])) +FunctionIterType = type(iter(lambda: 0, 0)) +DictProxyType = type(TypeType.__dict__) + del sys, _f, _C, _x # Not for export diff --git a/Makefile.pre.in b/Makefile.pre.in index 9fbf29d8f0c..ff22ef3e694 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -237,6 +237,7 @@ OBJECT_OBJS= \ Objects/classobject.o \ Objects/cobject.o \ Objects/complexobject.o \ + Objects/descrobject.o \ Objects/fileobject.o \ Objects/floatobject.o \ Objects/frameobject.o \ @@ -438,6 +439,7 @@ PYTHON_HEADERS= \ Include/tupleobject.h \ Include/listobject.h \ Include/iterobject.h \ + Include/descrobject.h \ Include/dictobject.h \ Include/methodobject.h \ Include/moduleobject.h \ diff --git a/Misc/NEWS b/Misc/NEWS index db4fbde8093..d93ab8658a3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -44,6 +44,17 @@ What's New in Python 2.2a1? Core +- TENTATIVELY, a large amount of code implementing much of what's + described in PEP 252 (Making Types Look More Like Classes) and PEP + 253 (Subtyping Built-in Types) was added. This will be released + with Python 2.2a1. Documentation will be provided separately + through http://www.python.org/2.2/. The purpose of releasing this + with Python 2.2a1 is to test backwards compatibility. It is + possible, though not likely, that a decision is made not to release + this code as part of 2.2 final, if any serious backwards + incompapatibilities are found during alpha testing that cannot be + repaired. + - Generators were added; this is a new way to create an iterator (see below) using what looks like a simple function containing one or more 'yield' statements. See PEP 255. Since this adds a new diff --git a/Modules/Setup.dist b/Modules/Setup.dist index 9114414f126..e9c2a949a6c 100644 --- a/Modules/Setup.dist +++ b/Modules/Setup.dist @@ -464,3 +464,5 @@ GLHACK=-Dclear=__GLclear # Example -- included for reference only: # xx xxmodule.c +# Another example -- the 'xxsubtype' module shows C-level subtyping in action +xxsubtype xxsubtype.c diff --git a/Modules/cPickle.c b/Modules/cPickle.c index e3cc58d07ef..b27339f568c 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -1869,6 +1869,10 @@ save(Picklerobject *self, PyObject *args, int pers_save) { res = save_tuple(self, args); goto finally; } + if (type == &PyType_Type) { + res = save_global(self, args, NULL); + goto finally; + } break; case 'l': diff --git a/Modules/config.c.in b/Modules/config.c.in index 5a5878fe9a0..0d5e8b061ac 100644 --- a/Modules/config.c.in +++ b/Modules/config.c.in @@ -37,7 +37,7 @@ struct _inittab _PyImport_Inittab[] = { {"__main__", NULL}, {"__builtin__", NULL}, {"sys", NULL}, - {"exceptions", init_exceptions}, + {"exceptions", NULL}, /* Sentinel */ {0, 0} diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c new file mode 100644 index 00000000000..60d8c7d4967 --- /dev/null +++ b/Modules/xxsubtype.c @@ -0,0 +1,233 @@ +#include "Python.h" + +/* Examples showing how to subtype the builtin list and dict types from C. */ + +/* spamlist -- a list subtype */ + +typedef struct { + PyListObject list; + int state; +} spamlistobject; + +static PyObject * +spamlist_getstate(spamlistobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getstate")) + return NULL; + return PyInt_FromLong(self->state); +} + +static PyObject * +spamlist_setstate(spamlistobject *self, PyObject *args) +{ + int state; + + if (!PyArg_ParseTuple(args, "i:setstate", &state)) + return NULL; + self->state = state; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef spamlist_methods[] = { + {"getstate", (PyCFunction)spamlist_getstate, METH_VARARGS, + "getstate() -> state"}, + {"setstate", (PyCFunction)spamlist_setstate, METH_VARARGS, + "setstate(state)"}, + {NULL, NULL}, +}; + +staticforward PyTypeObject spamlist_type; + +static int +spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds) +{ + if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + +static PyTypeObject spamlist_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "spamlist", + sizeof(spamlistobject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + spamlist_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyList_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)spamlist_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +/* spamdict -- a dict subtype */ + +typedef struct { + PyDictObject dict; + int state; +} spamdictobject; + +static PyObject * +spamdict_getstate(spamdictobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getstate")) + return NULL; + return PyInt_FromLong(self->state); +} + +static PyObject * +spamdict_setstate(spamdictobject *self, PyObject *args) +{ + int state; + + if (!PyArg_ParseTuple(args, "i:setstate", &state)) + return NULL; + self->state = state; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef spamdict_methods[] = { + {"getstate", (PyCFunction)spamdict_getstate, METH_VARARGS, + "getstate() -> state"}, + {"setstate", (PyCFunction)spamdict_setstate, METH_VARARGS, + "setstate(state)"}, + {NULL, NULL}, +}; + +staticforward PyTypeObject spamdict_type; + +static int +spamdict_init(spamdictobject *self, PyObject *args, PyObject *kwds) +{ + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + +static PyTypeObject spamdict_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "spamdict", + sizeof(spamdictobject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + spamdict_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyDict_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)spamdict_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject * +spam_bench(PyObject *self, PyObject *args) +{ + PyObject *obj, *name, *res; + int n = 1000; + time_t t0, t1; + + if (!PyArg_ParseTuple(args, "OS|i", &obj, &name, &n)) + return NULL; + t0 = clock(); + while (--n >= 0) { + res = PyObject_GetAttr(obj, name); + if (res == NULL) + return NULL; + Py_DECREF(res); + } + t1 = clock(); + return PyFloat_FromDouble((double)(t1-t0) / CLOCKS_PER_SEC); +} + +static PyMethodDef xxsubtype_functions[] = { + {"bench", spam_bench, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +DL_EXPORT(void) +initxxsubtype(void) +{ + PyObject *m, *d; + + m = Py_InitModule("xxsubtype", xxsubtype_functions); + if (m == NULL) + return; + + if (PyType_InitDict(&spamlist_type) < 0) + return; + if (PyType_InitDict(&spamdict_type) < 0) + return; + + d = PyModule_GetDict(m); + if (d == NULL) + return; + + Py_INCREF(&spamlist_type); + if (PyDict_SetItemString(d, "spamlist", + (PyObject *) &spamlist_type) < 0) + return; + + Py_INCREF(&spamdict_type); + if (PyDict_SetItemString(d, "spamdict", + (PyObject *) &spamdict_type) < 0) + return; +} diff --git a/Objects/abstract.c b/Objects/abstract.c index 63fe7d51225..a0f075f6081 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1588,6 +1588,24 @@ PyObject_CallObject(PyObject *o, PyObject *a) return r; } +PyObject * +PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) +{ + ternaryfunc call; + + if ((call = func->ob_type->tp_call) != NULL) { + PyObject *result = (*call)(func, arg, kw); + if (result == NULL && !PyErr_Occurred()) + PyErr_SetString( + PyExc_SystemError, + "NULL result without error in PyObject_Call"); + return result; + } + PyErr_Format(PyExc_TypeError, "object is not callable: %s", + PyString_AS_STRING(PyObject_Repr(func))); + return NULL; +} + PyObject * PyObject_CallFunction(PyObject *callable, char *format, ...) { @@ -1746,7 +1764,7 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls) } } else if (PyType_Check(cls)) { - retval = ((PyObject *)(inst->ob_type) == cls); + retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls); } else if (!PyInstance_Check(inst)) { if (__class__ == NULL) { diff --git a/Objects/bufferobject.c b/Objects/bufferobject.c index 1a17ec5ba8c..4c213ce975c 100644 --- a/Objects/bufferobject.c +++ b/Objects/bufferobject.c @@ -537,21 +537,21 @@ PyTypeObject PyBuffer_Type = { "buffer", sizeof(PyBufferObject), 0, - (destructor)buffer_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)buffer_compare, /*tp_compare*/ - (reprfunc)buffer_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - &buffer_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)buffer_hash, /*tp_hash*/ - 0, /*tp_call*/ - (reprfunc)buffer_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - &buffer_as_buffer, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ + (destructor)buffer_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)buffer_compare, /* tp_compare */ + (reprfunc)buffer_repr, /* tp_repr */ + 0, /* tp_as_number */ + &buffer_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)buffer_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)buffer_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &buffer_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ }; diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 66fc8d1d521..9a367764022 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -106,7 +106,7 @@ PyTypeObject PyCell_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ diff --git a/Objects/classobject.c b/Objects/classobject.c index 06f6714d9a9..c4b1d8e1f9b 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -36,12 +36,12 @@ PyClass_New(PyObject *bases, PyObject *dict, PyObject *name) return NULL; } if (name == NULL || !PyString_Check(name)) { - PyErr_SetString(PyExc_SystemError, + PyErr_SetString(PyExc_TypeError, "PyClass_New: name must be a string"); return NULL; } if (dict == NULL || !PyDict_Check(dict)) { - PyErr_SetString(PyExc_SystemError, + PyErr_SetString(PyExc_TypeError, "PyClass_New: dict must be a dictionary"); return NULL; } @@ -67,14 +67,14 @@ PyClass_New(PyObject *bases, PyObject *dict, PyObject *name) else { int i; if (!PyTuple_Check(bases)) { - PyErr_SetString(PyExc_SystemError, + PyErr_SetString(PyExc_TypeError, "PyClass_New: bases must be a tuple"); return NULL; } i = PyTuple_Size(bases); while (--i >= 0) { if (!PyClass_Check(PyTuple_GetItem(bases, i))) { - PyErr_SetString(PyExc_SystemError, + PyErr_SetString(PyExc_TypeError, "PyClass_New: base must be a class"); return NULL; } @@ -106,6 +106,18 @@ PyClass_New(PyObject *bases, PyObject *dict, PyObject *name) return (PyObject *) op; } +static PyObject * +class_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *name, *bases, *dict; + static char *kwlist[] = {"name", "bases", "dict", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "SOO", kwlist, + &name, &bases, &dict)) + return NULL; + return PyClass_New(bases, dict, name); +} + /* Class methods */ static void @@ -149,6 +161,8 @@ class_getattr(register PyClassObject *op, PyObject *name) register PyObject *v; register char *sname = PyString_AsString(name); PyClassObject *class; + descrgetfunc f; + if (sname[0] == '_' && sname[1] == '_') { if (strcmp(sname, "__dict__") == 0) { if (PyEval_GetRestricted()) { @@ -186,6 +200,11 @@ class_getattr(register PyClassObject *op, PyObject *name) Py_DECREF(v); v = w; } + f = v->ob_type->tp_descr_get; + if (f == NULL) + Py_INCREF(v); + else + v = f(v, (PyObject *)NULL, (PyObject *)op); return v; } @@ -396,7 +415,7 @@ PyTypeObject PyClass_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - 0, /* tp_call */ + PyInstance_New, /* tp_call */ (reprfunc)class_str, /* tp_str */ (getattrofunc)class_getattr, /* tp_getattro */ (setattrofunc)class_setattr, /* tp_setattro */ @@ -404,6 +423,22 @@ PyTypeObject PyClass_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)class_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + class_new, /* tp_new */ }; int @@ -531,7 +566,7 @@ instance_dealloc(register PyInstanceObject *inst) /* compensate for boost in _Py_NewReference; note that * _Py_RefTotal was also boosted; we'll knock that down later. */ - inst->ob_type->tp_alloc--; + inst->ob_type->tp_allocs--; #endif #else /* !Py_TRACE_REFS */ /* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */ @@ -564,7 +599,7 @@ instance_dealloc(register PyInstanceObject *inst) #endif if (--inst->ob_refcnt > 0) { #ifdef COUNT_ALLOCS - inst->ob_type->tp_free--; + inst->ob_type->tp_frees--; #endif return; /* __del__ added a reference; don't delete now */ } @@ -572,7 +607,7 @@ instance_dealloc(register PyInstanceObject *inst) _Py_ForgetReference((PyObject *)inst); #ifdef COUNT_ALLOCS /* compensate for increment in _Py_ForgetReference */ - inst->ob_type->tp_free--; + inst->ob_type->tp_frees--; #endif #ifndef WITH_CYCLE_GC inst->ob_type = NULL; @@ -619,6 +654,8 @@ instance_getattr2(register PyInstanceObject *inst, PyObject *name) { register PyObject *v; PyClassObject *class; + descrgetfunc f; + class = NULL; v = PyDict_GetItem(inst->in_dict, name); if (v == NULL) { @@ -628,17 +665,20 @@ instance_getattr2(register PyInstanceObject *inst, PyObject *name) } Py_INCREF(v); if (class != NULL) { - if (PyFunction_Check(v)) { - PyObject *w = PyMethod_New(v, (PyObject *)inst, - (PyObject *)class); + f = v->ob_type->tp_descr_get; + if (f != NULL) { + PyObject *w = f(v, (PyObject *)inst, + (PyObject *)(inst->in_class)); Py_DECREF(v); v = w; } else if (PyMethod_Check(v)) { - PyObject *im_class = PyMethod_Class(v); + /* XXX This should be a tp_descr_get slot of + PyMethodObjects */ + PyObject *im_class = PyMethod_GET_CLASS(v); /* Only if classes are compatible */ if (PyClass_IsSubclass((PyObject *)class, im_class)) { - PyObject *im_func = PyMethod_Function(v); + PyObject *im_func = PyMethod_GET_FUNCTION(v); PyObject *w = PyMethod_New(im_func, (PyObject *)inst, im_class); Py_DECREF(v); @@ -1814,6 +1854,23 @@ instance_iternext(PyInstanceObject *self) return NULL; } +static PyObject * +instance_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *res, *call = PyObject_GetAttrString(func, "__call__"); + if (call == NULL) { + PyInstanceObject *inst = (PyInstanceObject*) func; + PyErr_Clear(); + PyErr_Format(PyExc_AttributeError, + "%.200s instance has no __call__ method", + PyString_AsString(inst->in_class->cl_name)); + return NULL; + } + res = PyObject_Call(call, arg, kw); + Py_DECREF(call); + return res; +} + static PyNumberMethods instance_as_number = { (binaryfunc)instance_add, /* nb_add */ @@ -1868,7 +1925,7 @@ PyTypeObject PyInstance_Type = { &instance_as_sequence, /* tp_as_sequence */ &instance_as_mapping, /* tp_as_mapping */ (hashfunc)instance_hash, /* tp_hash */ - 0, /* tp_call */ + instance_call, /* tp_call */ (reprfunc)instance_str, /* tp_str */ (getattrofunc)instance_getattr, /* tp_getattro */ (setattrofunc)instance_setattr, /* tp_setattro */ @@ -1921,36 +1978,6 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *class) return (PyObject *)im; } -PyObject * -PyMethod_Function(register PyObject *im) -{ - if (!PyMethod_Check(im)) { - PyErr_BadInternalCall(); - return NULL; - } - return ((PyMethodObject *)im)->im_func; -} - -PyObject * -PyMethod_Self(register PyObject *im) -{ - if (!PyMethod_Check(im)) { - PyErr_BadInternalCall(); - return NULL; - } - return ((PyMethodObject *)im)->im_self; -} - -PyObject * -PyMethod_Class(register PyObject *im) -{ - if (!PyMethod_Check(im)) { - PyErr_BadInternalCall(); - return NULL; - } - return ((PyMethodObject *)im)->im_class; -} - /* Class method methods */ #define OFF(x) offsetof(PyMethodObject, x) @@ -2028,43 +2055,52 @@ instancemethod_compare(PyMethodObject *a, PyMethodObject *b) static PyObject * instancemethod_repr(PyMethodObject *a) { - char buf[240]; - PyInstanceObject *self = (PyInstanceObject *)(a->im_self); + char buffer[240]; + PyObject *self = a->im_self; PyObject *func = a->im_func; - PyClassObject *class = (PyClassObject *)(a->im_class); - PyObject *fclassname, *iclassname, *funcname; - char *fcname, *icname, *fname; - fclassname = class->cl_name; - if (PyFunction_Check(func)) { - funcname = ((PyFunctionObject *)func)->func_name; - Py_INCREF(funcname); + PyObject *klass = a->im_class; + PyObject *funcname = NULL, *klassname = NULL, *result = NULL; + char *sfuncname = "?", *sklassname = "?"; + + funcname = PyObject_GetAttrString(func, "__name__"); + if (funcname == NULL) + PyErr_Clear(); + else if (!PyString_Check(funcname)) { + Py_DECREF(funcname); + funcname = NULL; } - else { - funcname = PyObject_GetAttrString(func,"__name__"); - if (funcname == NULL) - PyErr_Clear(); + else + sfuncname = PyString_AS_STRING(funcname); + klassname = PyObject_GetAttrString(klass, "__name__"); + if (klassname == NULL) + PyErr_Clear(); + else if (!PyString_Check(klassname)) { + Py_DECREF(klassname); + klassname = NULL; } - if (funcname != NULL && PyString_Check(funcname)) - fname = PyString_AS_STRING(funcname); else - fname = "?"; - if (fclassname != NULL && PyString_Check(fclassname)) - fcname = PyString_AsString(fclassname); - else - fcname = "?"; + sklassname = PyString_AS_STRING(klassname); if (self == NULL) - sprintf(buf, "", fcname, fname); + sprintf(buffer, "", + sklassname, sfuncname); else { - iclassname = self->in_class->cl_name; - if (iclassname != NULL && PyString_Check(iclassname)) - icname = PyString_AsString(iclassname); - else - icname = "?"; - sprintf(buf, "", - fcname, fname, icname, self); + /* XXX Shouldn't use repr() here! */ + PyObject *selfrepr = PyObject_Repr(self); + if (selfrepr == NULL) + goto fail; + if (!PyString_Check(selfrepr)) { + Py_DECREF(selfrepr); + goto fail; + } + sprintf(buffer, "", + sklassname, sfuncname, PyString_AS_STRING(selfrepr)); + Py_DECREF(selfrepr); } + result = PyString_FromString(buffer); + fail: Py_XDECREF(funcname); - return PyString_FromString(buf); + Py_XDECREF(klassname); + return result; } static long @@ -2105,6 +2141,57 @@ instancemethod_traverse(PyMethodObject *im, visitproc visit, void *arg) return 0; } +static PyObject * +instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *self = PyMethod_GET_SELF(func); + PyObject *class = PyMethod_GET_CLASS(func); + PyObject *result; + + func = PyMethod_GET_FUNCTION(func); + if (self == NULL) { + /* Unbound methods must be called with an instance of + the class (or a derived class) as first argument */ + int ok; + if (PyTuple_Size(arg) >= 1) + self = PyTuple_GET_ITEM(arg, 0); + if (self == NULL) + ok = 0; + else { + ok = PyObject_IsInstance(self, class); + if (ok < 0) + return NULL; + } + if (!ok) { + PyErr_Format(PyExc_TypeError, + "unbound method %s%s must be " + "called with instance as first argument", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + return NULL; + } + Py_INCREF(arg); + } + else { + int argcount = PyTuple_Size(arg); + PyObject *newarg = PyTuple_New(argcount + 1); + int i; + if (newarg == NULL) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(newarg, 0, self); + for (i = 0; i < argcount; i++) { + PyObject *v = PyTuple_GET_ITEM(arg, i); + Py_XINCREF(v); + PyTuple_SET_ITEM(newarg, i+1, v); + } + arg = newarg; + } + result = PyObject_Call((PyObject *)func, arg, kw); + Py_DECREF(arg); + return result; +} + PyTypeObject PyMethod_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -2121,7 +2208,7 @@ PyTypeObject PyMethod_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)instancemethod_hash, /* tp_hash */ - 0, /* tp_call */ + instancemethod_call, /* tp_call */ 0, /* tp_str */ (getattrofunc)instancemethod_getattro, /* tp_getattro */ (setattrofunc)instancemethod_setattro, /* tp_setattro */ diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 34dbab0beca..9a66c0c593e 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -8,6 +8,7 @@ #ifndef WITHOUT_COMPLEX #include "Python.h" +#include "structmember.h" /* Precisions used by repr() and str(), respectively. @@ -182,6 +183,17 @@ c_powi(Py_complex x, long n) } +static PyObject * +complex_subtype_from_c_complex(PyTypeObject *type, Py_complex cval) +{ + PyObject *op; + + op = PyType_GenericAlloc(type, 0); + if (op != NULL) + ((PyComplexObject *)op)->cval = cval; + return op; +} + PyObject * PyComplex_FromCComplex(Py_complex cval) { @@ -196,6 +208,15 @@ PyComplex_FromCComplex(Py_complex cval) return (PyObject *) op; } +static PyObject * +complex_subtype_from_doubles(PyTypeObject *type, double real, double imag) +{ + Py_complex c; + c.real = real; + c.imag = imag; + return complex_subtype_from_c_complex(type, c); +} + PyObject * PyComplex_FromDoubles(double real, double imag) { @@ -559,19 +580,261 @@ static PyMethodDef complex_methods[] = { {NULL, NULL} /* sentinel */ }; +static struct memberlist complex_members[] = { + {"real", T_DOUBLE, offsetof(PyComplexObject, cval.real), 0}, + {"imag", T_DOUBLE, offsetof(PyComplexObject, cval.imag), 0}, + {0}, +}; static PyObject * -complex_getattr(PyComplexObject *self, char *name) +complex_subtype_from_string(PyTypeObject *type, PyObject *v) { - if (strcmp(name, "real") == 0) - return (PyObject *)PyFloat_FromDouble(self->cval.real); - else if (strcmp(name, "imag") == 0) - return (PyObject *)PyFloat_FromDouble(self->cval.imag); - else if (strcmp(name, "__members__") == 0) - return Py_BuildValue("[ss]", "imag", "real"); - return Py_FindMethod(complex_methods, (PyObject *)self, name); + extern double strtod(const char *, char **); + const char *s, *start; + char *end; + double x=0.0, y=0.0, z; + int got_re=0, got_im=0, done=0; + int digit_or_dot; + int sw_error=0; + int sign; + char buffer[256]; /* For errors */ + char s_buffer[256]; + int len; + + if (PyString_Check(v)) { + s = PyString_AS_STRING(v); + len = PyString_GET_SIZE(v); + } + else if (PyUnicode_Check(v)) { + if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { + PyErr_SetString(PyExc_ValueError, + "complex() literal too large to convert"); + return NULL; + } + if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v), + s_buffer, + NULL)) + return NULL; + s = s_buffer; + len = (int)strlen(s); + } + else if (PyObject_AsCharBuffer(v, &s, &len)) { + PyErr_SetString(PyExc_TypeError, + "complex() arg is not a string"); + return NULL; + } + + /* position on first nonblank */ + start = s; + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, + "complex() arg is an empty string"); + return NULL; + } + + z = -1.0; + sign = 1; + do { + + switch (*s) { + + case '\0': + if (s-start != len) { + PyErr_SetString( + PyExc_ValueError, + "complex() arg contains a null byte"); + return NULL; + } + if(!done) sw_error=1; + break; + + case '-': + sign = -1; + /* Fallthrough */ + case '+': + if (done) sw_error=1; + s++; + if ( *s=='\0'||*s=='+'||*s=='-' || + isspace(Py_CHARMASK(*s)) ) sw_error=1; + break; + + case 'J': + case 'j': + if (got_im || done) { + sw_error = 1; + break; + } + if (z<0.0) { + y=sign; + } + else{ + y=sign*z; + } + got_im=1; + s++; + if (*s!='+' && *s!='-' ) + done=1; + break; + + default: + if (isspace(Py_CHARMASK(*s))) { + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] != '\0') + sw_error=1; + else + done = 1; + break; + } + digit_or_dot = + (*s=='.' || isdigit(Py_CHARMASK(*s))); + if (done||!digit_or_dot) { + sw_error=1; + break; + } + errno = 0; + PyFPE_START_PROTECT("strtod", return 0) + z = strtod(s, &end) ; + PyFPE_END_PROTECT(z) + if (errno != 0) { + sprintf(buffer, + "float() out of range: %.150s", s); + PyErr_SetString( + PyExc_ValueError, + buffer); + return NULL; + } + s=end; + if (*s=='J' || *s=='j') { + + break; + } + if (got_re) { + sw_error=1; + break; + } + + /* accept a real part */ + x=sign*z; + got_re=1; + if (got_im) done=1; + z = -1.0; + sign = 1; + break; + + } /* end of switch */ + + } while (*s!='\0' && !sw_error); + + if (sw_error) { + PyErr_SetString(PyExc_ValueError, + "complex() arg is a malformed string"); + return NULL; + } + + return complex_subtype_from_doubles(type, x, y); } +static PyObject * +complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *r, *i, *tmp; + PyNumberMethods *nbr, *nbi = NULL; + Py_complex cr, ci; + int own_r = 0; + static char *kwlist[] = {"real", "imag", 0}; + + r = Py_False; + i = NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:complex", kwlist, + &r, &i)) + return NULL; + if (PyString_Check(r) || PyUnicode_Check(r)) + return complex_subtype_from_string(type, r); + if ((nbr = r->ob_type->tp_as_number) == NULL || + nbr->nb_float == NULL || + (i != NULL && + ((nbi = i->ob_type->tp_as_number) == NULL || + nbi->nb_float == NULL))) { + PyErr_SetString(PyExc_TypeError, + "complex() arg can't be converted to complex"); + return NULL; + } + /* XXX Hack to support classes with __complex__ method */ + if (PyInstance_Check(r)) { + static PyObject *complexstr; + PyObject *f; + if (complexstr == NULL) { + complexstr = PyString_InternFromString("__complex__"); + if (complexstr == NULL) + return NULL; + } + f = PyObject_GetAttr(r, complexstr); + if (f == NULL) + PyErr_Clear(); + else { + PyObject *args = Py_BuildValue("()"); + if (args == NULL) + return NULL; + r = PyEval_CallObject(f, args); + Py_DECREF(args); + Py_DECREF(f); + if (r == NULL) + return NULL; + own_r = 1; + } + } + if (PyComplex_Check(r)) { + cr = ((PyComplexObject*)r)->cval; + if (own_r) { + Py_DECREF(r); + } + } + else { + tmp = PyNumber_Float(r); + if (own_r) { + Py_DECREF(r); + } + if (tmp == NULL) + return NULL; + if (!PyFloat_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "float(r) didn't return a float"); + Py_DECREF(tmp); + return NULL; + } + cr.real = PyFloat_AsDouble(tmp); + Py_DECREF(tmp); + cr.imag = 0.0; + } + if (i == NULL) { + ci.real = 0.0; + ci.imag = 0.0; + } + else if (PyComplex_Check(i)) + ci = ((PyComplexObject*)i)->cval; + else { + tmp = (*nbi->nb_float)(i); + if (tmp == NULL) + return NULL; + ci.real = PyFloat_AsDouble(tmp); + Py_DECREF(tmp); + ci.imag = 0.; + } + cr.real -= ci.imag; + cr.imag += ci.real; + return complex_subtype_from_c_complex(type, cr); +} + +static char complex_doc[] = +"complex(real[, imag]) -> complex number\n\ +\n\ +Create a complex number from a real part and an optional imaginary part.\n\ +This is equivalent to (real + imag*1j) where imag defaults to 0."; + static PyNumberMethods complex_as_number = { (binaryfunc)complex_add, /* nb_add */ (binaryfunc)complex_sub, /* nb_subtract */ @@ -606,7 +869,7 @@ PyTypeObject PyComplex_Type = { 0, (destructor)complex_dealloc, /* tp_dealloc */ (printfunc)complex_print, /* tp_print */ - (getattrfunc)complex_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)complex_repr, /* tp_repr */ @@ -616,14 +879,28 @@ PyTypeObject PyComplex_Type = { (hashfunc)complex_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)complex_str, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + complex_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ complex_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + complex_methods, /* tp_methods */ + complex_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + complex_new, /* tp_new */ }; #endif diff --git a/Objects/descrobject.c b/Objects/descrobject.c new file mode 100644 index 00000000000..40629f6c24a --- /dev/null +++ b/Objects/descrobject.c @@ -0,0 +1,854 @@ +/* Descriptors -- a new, flexible way to describe attributes */ + +#include "Python.h" +#include "structmember.h" /* Why is this not included in Python.h? */ + +/* Various kinds of descriptor objects */ + +#define COMMON \ + PyObject_HEAD \ + PyTypeObject *d_type; \ + PyObject *d_name + +typedef struct { + COMMON; +} PyDescrObject; + +typedef struct { + COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + COMMON; + struct memberlist *d_member; +} PyMemberDescrObject; + +typedef struct { + COMMON; + struct getsetlist *d_getset; +} PyGetSetDescrObject; + +typedef struct { + COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; + +static void +descr_dealloc(PyDescrObject *descr) +{ + Py_XDECREF(descr->d_type); + Py_XDECREF(descr->d_name); + PyObject_DEL(descr); +} + +static char * +descr_name(PyDescrObject *descr) +{ + if (descr->d_name != NULL && PyString_Check(descr->d_name)) + return PyString_AS_STRING(descr->d_name); + else + return "?"; +} + +static PyObject * +descr_repr(PyDescrObject *descr, char *format) +{ + char buffer[500]; + + sprintf(buffer, format, descr_name(descr), descr->d_type->tp_name); + return PyString_FromString(buffer); +} + +static PyObject * +method_repr(PyMethodDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +member_repr(PyMemberDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +getset_repr(PyGetSetDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +wrapper_repr(PyWrapperDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static int +descr_check(PyDescrObject *descr, PyObject *obj, PyTypeObject *type, + PyObject **pres) +{ + if (obj == NULL || obj == Py_None) { + Py_INCREF(descr); + *pres = (PyObject *)descr; + return 1; + } + if (!PyObject_IsInstance(obj, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' for '%.100s' objects " + "doesn't apply to '%.100s' object", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + obj->ob_type->tp_name); + *pres = NULL; + return 1; + } + return 0; +} + +static PyObject * +method_get(PyMethodDescrObject *descr, PyObject *obj, PyTypeObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, type, &res)) + return res; + return PyCFunction_New(descr->d_method, obj); +} + +static PyObject * +member_get(PyMemberDescrObject *descr, PyObject *obj, PyTypeObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, type, &res)) + return res; + return PyMember_Get((char *)obj, descr->d_member, + descr->d_member->name); +} + +static PyObject * +getset_get(PyGetSetDescrObject *descr, PyObject *obj, PyTypeObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, type, &res)) + return res; + if (descr->d_getset->get != NULL) + return descr->d_getset->get(obj, descr->d_getset->closure); + PyErr_Format(PyExc_TypeError, + "attribute '%300s' of '%.100s' objects is not readable", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; +} + +static PyObject * +wrapper_get(PyWrapperDescrObject *descr, PyObject *obj, PyTypeObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, type, &res)) + return res; + return PyWrapper_New((PyObject *)descr, obj); +} + +static int +descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value, + int *pres) +{ + assert(obj != NULL); + if (!PyObject_IsInstance(obj, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' for '%.100s' objects " + "doesn't apply to '%.100s' object", + descr_name(descr), + descr->d_type->tp_name, + obj->ob_type->tp_name); + *pres = -1; + return 1; + } + return 0; +} + +static int +member_set(PyMemberDescrObject *descr, PyObject *obj, PyObject *value) +{ + int res; + + if (descr_setcheck((PyDescrObject *)descr, obj, value, &res)) + return res; + return PyMember_Set((char *)obj, descr->d_member, + descr->d_member->name, value); +} + +static int +getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) +{ + int res; + + if (descr_setcheck((PyDescrObject *)descr, obj, value, &res)) + return res; + if (descr->d_getset->set != NULL) + return descr->d_getset->set(obj, value, + descr->d_getset->closure); + PyErr_Format(PyExc_TypeError, + "attribute '%300s' of '%.100s' objects is not writable", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return -1; +} + +static PyObject * +methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) +{ + int argc; + PyObject *self, *func, *result; + + /* Make sure that the first argument is acceptable as 'self' */ + assert(PyTuple_Check(args)); + argc = PyTuple_GET_SIZE(args); + if (argc < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.300s' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + self->ob_type->tp_name); + return NULL; + } + + func = PyCFunction_New(descr->d_method, self); + if (func == NULL) + return NULL; + args = PyTuple_GetSlice(args, 1, argc); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObjectWithKeywords(func, args, kwds); + Py_DECREF(args); + Py_DECREF(func); + return result; +} + +static PyObject * +wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) +{ + int argc; + PyObject *self, *func, *result; + + /* Make sure that the first argument is acceptable as 'self' */ + assert(PyTuple_Check(args)); + argc = PyTuple_GET_SIZE(args); + if (argc < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.300s' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + self->ob_type->tp_name); + return NULL; + } + + func = PyWrapper_New((PyObject *)descr, self); + if (func == NULL) + return NULL; + args = PyTuple_GetSlice(args, 1, argc); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObjectWithKeywords(func, args, kwds); + Py_DECREF(args); + Py_DECREF(func); + return result; +} + +static PyObject * +member_get_doc(PyMethodDescrObject *descr, void *closure) +{ + if (descr->d_method->ml_doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_method->ml_doc); +} + +static struct memberlist descr_members[] = { + {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY}, + {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY}, + {0} +}; + +static struct getsetlist member_getset[] = { + {"__doc__", (getter)member_get_doc}, + {0} +}; + +static PyObject * +wrapper_get_doc(PyWrapperDescrObject *descr, void *closure) +{ + if (descr->d_base->doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_base->doc); +} + +static struct getsetlist wrapper_getset[] = { + {"__doc__", (getter)wrapper_get_doc}, + {0} +}; + +static PyTypeObject PyMethodDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "method_descriptor", + sizeof(PyMethodDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)method_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)methoddescr_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + member_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)method_get, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +static PyTypeObject PyMemberDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "member_descriptor", + sizeof(PyMemberDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)member_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)member_get, /* tp_descr_get */ + (descrsetfunc)member_set, /* tp_descr_set */ +}; + +static PyTypeObject PyGetSetDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "getset_descriptor", + sizeof(PyGetSetDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)getset_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)getset_get, /* tp_descr_get */ + (descrsetfunc)getset_set, /* tp_descr_set */ +}; + +static PyTypeObject PyWrapperDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "wrapper_descriptor", + sizeof(PyWrapperDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)wrapper_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)wrapperdescr_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + wrapper_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)wrapper_get, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +static PyDescrObject * +descr_new(PyTypeObject *descrtype, PyTypeObject *type, char *name) +{ + PyDescrObject *descr; + + descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0); + if (descr != NULL) { + Py_XINCREF(type); + descr->d_type = type; + descr->d_name = PyString_InternFromString(name); + if (descr->d_name == NULL) { + Py_DECREF(descr); + descr = NULL; + } + } + return descr; +} + +PyObject * +PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) +{ + PyMethodDescrObject *descr; + + descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type, + type, method->ml_name); + if (descr != NULL) + descr->d_method = method; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewMember(PyTypeObject *type, struct memberlist *member) +{ + PyMemberDescrObject *descr; + + descr = (PyMemberDescrObject *)descr_new(&PyMemberDescr_Type, + type, member->name); + if (descr != NULL) + descr->d_member = member; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewGetSet(PyTypeObject *type, struct getsetlist *getset) +{ + PyGetSetDescrObject *descr; + + descr = (PyGetSetDescrObject *)descr_new(&PyGetSetDescr_Type, + type, getset->name); + if (descr != NULL) + descr->d_getset = getset; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped) +{ + PyWrapperDescrObject *descr; + + descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type, + type, base->name); + if (descr != NULL) { + descr->d_base = base; + descr->d_wrapped = wrapped; + } + return (PyObject *)descr; +} + +int +PyDescr_IsData(PyObject *d) +{ + return d->ob_type->tp_descr_set != NULL; +} + + +/* --- Readonly proxy for dictionaries (actually any mapping) --- */ + +/* This has no reason to be in this file except that adding new files is a + bit of a pain */ + +typedef struct { + PyObject_HEAD + PyObject *dict; +} proxyobject; + +static int +proxy_len(proxyobject *pp) +{ + return PyObject_Size(pp->dict); +} + +static PyObject * +proxy_getitem(proxyobject *pp, PyObject *key) +{ + return PyObject_GetItem(pp->dict, key); +} + +static PyMappingMethods proxy_as_mapping = { + (inquiry)proxy_len, /* mp_length */ + (binaryfunc)proxy_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static int +proxy_contains(proxyobject *pp, PyObject *key) +{ + return PySequence_Contains(pp->dict, key); +} + +static PySequenceMethods proxy_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)proxy_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +proxy_has_key(proxyobject *pp, PyObject *args) +{ + PyObject *key; + + if (!PyArg_ParseTuple(args, "O:has_key", &key)) + return NULL; + return PyInt_FromLong(PySequence_Contains(pp->dict, key)); +} + +static PyObject * +proxy_get(proxyobject *pp, PyObject *args) +{ + PyObject *key, *def = Py_None; + + if (!PyArg_ParseTuple(args, "O|O:get", &key, &def)) + return NULL; + return PyObject_CallMethod(pp->dict, "get", "(OO)", key, def); +} + +static PyObject * +proxy_keys(proxyobject *pp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":keys")) + return NULL; + return PyMapping_Keys(pp->dict); +} + +static PyObject * +proxy_values(proxyobject *pp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":values")) + return NULL; + return PyMapping_Values(pp->dict); +} + +static PyObject * +proxy_items(proxyobject *pp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":items")) + return NULL; + return PyMapping_Items(pp->dict); +} + +static PyObject * +proxy_copy(proxyobject *pp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":copy")) + return NULL; + return PyObject_CallMethod(pp->dict, "copy", NULL); +} + +static PyMethodDef proxy_methods[] = { + {"has_key", (PyCFunction)proxy_has_key, METH_VARARGS, "XXX"}, + {"get", (PyCFunction)proxy_get, METH_VARARGS, "XXX"}, + {"keys", (PyCFunction)proxy_keys, METH_VARARGS, "XXX"}, + {"values", (PyCFunction)proxy_values, METH_VARARGS, "XXX"}, + {"items", (PyCFunction)proxy_items, METH_VARARGS, "XXX"}, + {"copy", (PyCFunction)proxy_copy, METH_VARARGS, "XXX"}, + {0} +}; + +static void +proxy_dealloc(proxyobject *pp) +{ + Py_DECREF(pp->dict); + PyObject_DEL(pp); +} + +static PyObject * +proxy_getiter(proxyobject *pp) +{ + return PyObject_GetIter(pp->dict); +} + +PyObject * +proxy_str(proxyobject *pp) +{ + return PyObject_Str(pp->dict); +} + +PyTypeObject proxytype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "dict-proxy", /* tp_name */ + sizeof(proxyobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)proxy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &proxy_as_sequence, /* tp_as_sequence */ + &proxy_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)proxy_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)proxy_getiter, /* tp_iter */ + 0, /* tp_iternext */ + proxy_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +PyObject * +PyDictProxy_New(PyObject *dict) +{ + proxyobject *pp; + + pp = PyObject_NEW(proxyobject, &proxytype); + if (pp != NULL) { + Py_INCREF(dict); + pp->dict = dict; + } + return (PyObject *)pp; +} + + +/* --- Wrapper object for "slot" methods --- */ + +/* This has no reason to be in this file except that adding new files is a + bit of a pain */ + +typedef struct { + PyObject_HEAD + PyWrapperDescrObject *descr; + PyObject *self; +} wrapperobject; + +static void +wrapper_dealloc(wrapperobject *wp) +{ + Py_XDECREF(wp->descr); + Py_XDECREF(wp->self); + PyObject_DEL(wp); +} + +static PyMethodDef wrapper_methods[] = { + {0} +}; + +static PyObject * +wrapper_name(wrapperobject *wp) +{ + char *s = wp->descr->d_base->name; + + return PyString_FromString(s); +} + +static PyObject * +wrapper_doc(wrapperobject *wp) +{ + char *s = wp->descr->d_base->doc; + + if (s == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else { + return PyString_FromString(s); + } +} + +static struct getsetlist wrapper_getsets[] = { + {"__name__", (getter)wrapper_name}, + {"__doc__", (getter)wrapper_doc}, + {0} +}; + +static PyObject * +wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds) +{ + wrapperfunc wrapper = wp->descr->d_base->wrapper; + PyObject *self = wp->self; + + return (*wrapper)(self, args, wp->descr->d_wrapped); +} + +PyTypeObject wrappertype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "method-wrapper", /* tp_name */ + sizeof(wrapperobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)wrapper_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)wrapper_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + wrapper_methods, /* tp_methods */ + 0, /* tp_members */ + wrapper_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +PyObject * +PyWrapper_New(PyObject *d, PyObject *self) +{ + wrapperobject *wp; + PyWrapperDescrObject *descr; + + assert(PyObject_TypeCheck(d, &PyWrapperDescr_Type)); + descr = (PyWrapperDescrObject *)d; + assert(PyObject_IsInstance(self, (PyObject *)(descr->d_type))); + + wp = PyObject_NEW(wrapperobject, &wrappertype); + if (wp != NULL) { + Py_INCREF(descr); + wp->descr = descr; + Py_INCREF(self); + wp->self = self; + } + return (PyObject *)wp; +} diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c17664efc5f..ce4c578f421 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3,15 +3,8 @@ #include "Python.h" -/* MINSIZE is the minimum size of a dictionary. This many slots are - * allocated directly in the dict object (in the ma_smalltable member). - * It must be a power of 2, and at least 4. 8 allows dicts with no more than - * 5 active entries to live in ma_smalltable (and so avoid an additional - * malloc); instrumentation suggested this suffices for the majority of - * dicts (consisting mostly of usually-small instance dicts and usually-small - * dicts created to pass keyword arguments). - */ -#define MINSIZE 8 +typedef PyDictEntry dictentry; +typedef PyDictObject dictobject; /* Define this out if you don't want conversion statistics on exit. */ #undef SHOW_CONVERSION_COUNTS @@ -116,69 +109,6 @@ equally good collision statistics, needed less code & used less memory. /* Object used as dummy key to fill deleted entries */ static PyObject *dummy; /* Initialized by first call to newdictobject() */ -/* -There are three kinds of slots in the table: - -1. Unused. me_key == me_value == NULL - Does not hold an active (key, value) pair now and never did. Unused can - transition to Active upon key insertion. This is the only case in which - me_key is NULL, and is each slot's initial state. - -2. Active. me_key != NULL and me_key != dummy and me_value != NULL - Holds an active (key, value) pair. Active can transition to Dummy upon - key deletion. This is the only case in which me_value != NULL. - -3. Dummy. me_key == dummy and me_value == NULL - Previously held an active (key, value) pair, but that was deleted and an - active pair has not yet overwritten the slot. Dummy can transition to - Active upon key insertion. Dummy slots cannot be made Unused again - (cannot have me_key set to NULL), else the probe sequence in case of - collision would have no way to know they were once active. - -Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to -hold a search finger. The me_hash field of Unused or Dummy slots has no -meaning otherwise. -*/ -typedef struct { - long me_hash; /* cached hash code of me_key */ - PyObject *me_key; - PyObject *me_value; -#ifdef USE_CACHE_ALIGNED - long aligner; -#endif -} dictentry; - -/* -To ensure the lookup algorithm terminates, there must be at least one Unused -slot (NULL key) in the table. -The value ma_fill is the number of non-NULL keys (sum of Active and Dummy); -ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL -values == the number of Active items). -To avoid slowing down lookups on a near-full table, we resize the table when -it's two-thirds full. -*/ -typedef struct dictobject dictobject; -struct dictobject { - PyObject_HEAD - int ma_fill; /* # Active + # Dummy */ - int ma_used; /* # Active */ - - /* The table contains ma_mask + 1 slots, and that's a power of 2. - * We store the mask instead of the size because the mask is more - * frequently needed. - */ - int ma_mask; - - /* ma_table points to ma_smalltable for small tables, else to - * additional malloc'ed memory. ma_table is never NULL! This rule - * saves repeated runtime null-tests in the workhorse getitem and - * setitem calls. - */ - dictentry *ma_table; - dictentry *(*ma_lookup)(dictobject *mp, PyObject *key, long hash); - dictentry ma_smalltable[MINSIZE]; -}; - /* forward declarations */ static dictentry * lookdict_string(dictobject *mp, PyObject *key, long hash); @@ -196,12 +126,24 @@ show_counts(void) } #endif -/* Set dictobject* mp to empty but w/ MINSIZE slots, using ma_smalltable. */ -#define empty_to_minsize(mp) do { \ - memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \ +/* Initialization macros. + There are two ways to create a dict: PyDict_New() is the main C API + function, and the tp_new slot maps to dict_new(). In the latter case we + can save a little time over what PyDict_New does because it's guaranteed + that the PyDictObject struct is already zeroed out. + Everyone except dict_new() should use EMPTY_TO_MINSIZE (unless they have + an excellent reason not to). +*/ + +#define INIT_NONZERO_DICT_SLOTS(mp) do { \ (mp)->ma_table = (mp)->ma_smalltable; \ - (mp)->ma_mask = MINSIZE - 1; \ + (mp)->ma_mask = PyDict_MINSIZE - 1; \ + } while(0) + +#define EMPTY_TO_MINSIZE(mp) do { \ + memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \ (mp)->ma_used = (mp)->ma_fill = 0; \ + INIT_NONZERO_DICT_SLOTS(mp); \ } while(0) PyObject * @@ -219,7 +161,7 @@ PyDict_New(void) mp = PyObject_NEW(dictobject, &PyDict_Type); if (mp == NULL) return NULL; - empty_to_minsize(mp); + EMPTY_TO_MINSIZE(mp); mp->ma_lookup = lookdict_string; #ifdef SHOW_CONVERSION_COUNTS ++created; @@ -418,7 +360,10 @@ insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value) { PyObject *old_value; register dictentry *ep; - ep = (mp->ma_lookup)(mp, key, hash); + typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, long); + + assert(mp->ma_lookup != NULL); + ep = mp->ma_lookup(mp, key, hash); if (ep->me_value != NULL) { old_value = ep->me_value; ep->me_value = value; @@ -449,12 +394,12 @@ dictresize(dictobject *mp, int minused) dictentry *oldtable, *newtable, *ep; int i; int is_oldtable_malloced; - dictentry small_copy[MINSIZE]; + dictentry small_copy[PyDict_MINSIZE]; assert(minused >= 0); /* Find the smallest table size > minused. */ - for (newsize = MINSIZE; + for (newsize = PyDict_MINSIZE; newsize <= minused && newsize > 0; newsize <<= 1) ; @@ -468,7 +413,7 @@ dictresize(dictobject *mp, int minused) assert(oldtable != NULL); is_oldtable_malloced = oldtable != mp->ma_smalltable; - if (newsize == MINSIZE) { + if (newsize == PyDict_MINSIZE) { /* A large table is shrinking, or we can't get any smaller. */ newtable = mp->ma_smalltable; if (newtable == oldtable) { @@ -649,7 +594,7 @@ PyDict_Clear(PyObject *op) dictentry *ep, *table; int table_is_malloced; int fill; - dictentry small_copy[MINSIZE]; + dictentry small_copy[PyDict_MINSIZE]; #ifdef Py_DEBUG int i, n; #endif @@ -674,7 +619,7 @@ PyDict_Clear(PyObject *op) */ fill = mp->ma_fill; if (table_is_malloced) - empty_to_minsize(mp); + EMPTY_TO_MINSIZE(mp); else if (fill > 0) { /* It's a small table with something that needs to be cleared. @@ -683,7 +628,7 @@ PyDict_Clear(PyObject *op) */ memcpy(small_copy, table, sizeof(small_copy)); table = small_copy; - empty_to_minsize(mp); + EMPTY_TO_MINSIZE(mp); } /* else it's a small table that's already empty */ @@ -1042,32 +987,47 @@ dict_items(register dictobject *mp, PyObject *args) } static PyObject * -dict_update(register dictobject *mp, PyObject *args) +dict_update(PyObject *mp, PyObject *args) { + PyObject *other; + + if (!PyArg_ParseTuple(args, "O:update", &other)) + return NULL; + if (PyDict_Update(mp, other) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +int +PyDict_Update(PyObject *a, PyObject *b) +{ + register PyDictObject *mp, *other; register int i; - dictobject *other; dictentry *entry; - PyObject *param; + /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ - if (!PyArg_ParseTuple(args, "O:update", ¶m)) - return NULL; - - if (PyDict_Check(param)) { - other = (dictobject*)param; + if (a == NULL || !PyDict_Check(a) || b == NULL) { + PyErr_BadInternalCall(); + return -1; + } + mp = (dictobject*)a; + if (PyDict_Check(b)) { + other = (dictobject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ - goto done; + return 0; /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if ((mp->ma_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) { if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0) - return NULL; + return -1; } for (i = 0; i <= other->ma_mask; i++) { entry = &other->ma_table[i]; @@ -1081,7 +1041,7 @@ dict_update(register dictobject *mp, PyObject *args) } else { /* Do it the generic, slower way */ - PyObject *keys = PyMapping_Keys(param); + PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; @@ -1092,37 +1052,34 @@ dict_update(register dictobject *mp, PyObject *args) * AttributeError to percolate up. Might as well * do the same for any other error. */ - return NULL; + return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) - return NULL; + return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { - value = PyObject_GetItem(param, key); + value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); - return NULL; + return -1; } status = PyDict_SetItem((PyObject*)mp, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); - return NULL; + return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ - return NULL; + return -1; } - - done: - Py_INCREF(Py_None); - return Py_None; + return 0; } static PyObject * @@ -1694,12 +1651,6 @@ static PyMethodDef mapp_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -dict_getattr(dictobject *mp, char *name) -{ - return Py_FindMethod(mapp_methods, (PyObject *)mp, name); -} - static int dict_contains(dictobject *mp, PyObject *key) { @@ -1731,6 +1682,26 @@ static PySequenceMethods dict_as_sequence = { 0, /* sq_inplace_repeat */ }; +static PyObject * +dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *self; + + assert(type != NULL && type->tp_alloc != NULL); + self = type->tp_alloc(type, 0); + if (self != NULL) { + PyDictObject *d = (PyDictObject *)self; + /* It's guaranteed that tp->alloc zeroed out the struct. */ + assert(d->ma_table == NULL && d->ma_fill == 0 && d->ma_used == 0); + INIT_NONZERO_DICT_SLOTS(d); + d->ma_lookup = lookdict_string; +#ifdef SHOW_CONVERSION_COUNTS + ++created; +#endif + } + return self; +} + static PyObject * dict_iter(dictobject *dict) { @@ -1745,7 +1716,7 @@ PyTypeObject PyDict_Type = { 0, (destructor)dict_dealloc, /* tp_dealloc */ (printfunc)dict_print, /* tp_print */ - (getattrfunc)dict_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)dict_compare, /* tp_compare */ (reprfunc)dict_repr, /* tp_repr */ @@ -1755,17 +1726,29 @@ PyTypeObject PyDict_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ - 0, /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + "dictionary type", /* tp_doc */ (traverseproc)dict_traverse, /* tp_traverse */ (inquiry)dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ + mapp_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + dict_new, /* tp_new */ }; /* For backward compatibility with old dictionary interface */ @@ -1873,12 +1856,6 @@ static PyMethodDef dictiter_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -dictiter_getattr(dictiterobject *di, char *name) -{ - return Py_FindMethod(dictiter_methods, (PyObject *)di, name); -} - static PyObject *dictiter_iternext(dictiterobject *di) { PyObject *key, *value; @@ -1903,7 +1880,7 @@ PyTypeObject PyDictIter_Type = { /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)dictiter_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ @@ -1913,7 +1890,7 @@ PyTypeObject PyDictIter_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ @@ -1924,4 +1901,11 @@ PyTypeObject PyDictIter_Type = { 0, /* tp_weaklistoffset */ (getiterfunc)dictiter_getiter, /* tp_iter */ (iternextfunc)dictiter_iternext, /* tp_iternext */ + dictiter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ }; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 18d15e1ae20..e192e8b9c77 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -1273,29 +1273,15 @@ static struct memberlist file_memberlist[] = { }; static PyObject * -file_getattr(PyFileObject *f, char *name) +get_closed(PyFileObject *f, void *closure) { - PyObject *res; - - res = Py_FindMethod(file_methods, (PyObject *)f, name); - if (res != NULL) - return res; - PyErr_Clear(); - if (strcmp(name, "closed") == 0) - return PyInt_FromLong((long)(f->f_fp == 0)); - return PyMember_Get((char *)f, file_memberlist, name); + return PyInt_FromLong((long)(f->f_fp == 0)); } -static int -file_setattr(PyFileObject *f, char *name, PyObject *v) -{ - if (v == NULL) { - PyErr_SetString(PyExc_AttributeError, - "can't delete file attributes"); - return -1; - } - return PyMember_Set((char *)f, file_memberlist, name, v); -} +static struct getsetlist file_getsetlist[] = { + {"closed", (getter)get_closed, NULL, NULL}, + {0}, +}; static PyObject * file_getiter(PyObject *f) @@ -1311,27 +1297,32 @@ PyTypeObject PyFile_Type = { 0, (destructor)file_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)file_getattr, /* tp_getattr */ - (setattrfunc)file_setattr, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /* tp_compare */ - (reprfunc)file_repr, /* tp_repr */ + (reprfunc)file_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ file_getiter, /* tp_iter */ 0, /* tp_iternext */ + file_methods, /* tp_methods */ + file_memberlist, /* tp_members */ + file_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; /* Interface for the 'soft space' between print items. */ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d1ce0927124..df88736abae 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -636,6 +636,26 @@ float_float(PyObject *v) } +static PyObject * +float_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = Py_False; /* Integer zero */ + static char *kwlist[] = {"x", 0}; + + assert(type == &PyFloat_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:float", kwlist, &x)) + return NULL; + if (PyString_Check(x)) + return PyFloat_FromString(x, NULL); + return PyNumber_Float(x); +} + +static char float_doc[] = +"float(x) -> floating point number\n\ +\n\ +Convert a string or number to a floating point number, if possible."; + + static PyNumberMethods float_as_number = { (binaryfunc)float_add, /*nb_add*/ (binaryfunc)float_sub, /*nb_subtract*/ @@ -679,22 +699,40 @@ PyTypeObject PyFloat_Type = { "float", sizeof(PyFloatObject), 0, - (destructor)float_dealloc, /*tp_dealloc*/ - (printfunc)float_print, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)float_compare, /*tp_compare*/ - (reprfunc)float_repr, /*tp_repr*/ - &float_as_number, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)float_hash, /*tp_hash*/ - 0, /*tp_call*/ - (reprfunc)float_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_CHECKTYPES /*tp_flags*/ + (destructor)float_dealloc, /* tp_dealloc */ + (printfunc)float_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)float_compare, /* tp_compare */ + (reprfunc)float_repr, /* tp_repr */ + &float_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)float_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)float_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + float_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + float_new, /* tp_new */ }; void diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0801b932c51..d3276166186 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -15,7 +15,6 @@ static struct memberlist frame_memberlist[] = { {"f_code", T_OBJECT, OFF(f_code), RO}, {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, {"f_globals", T_OBJECT, OFF(f_globals), RO}, - {"f_locals", T_OBJECT, OFF(f_locals), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_lineno", T_INT, OFF(f_lineno), RO}, {"f_restricted",T_INT, OFF(f_restricted),RO}, @@ -27,18 +26,17 @@ static struct memberlist frame_memberlist[] = { }; static PyObject * -frame_getattr(PyFrameObject *f, char *name) +frame_getlocals(PyFrameObject *f, void *closure) { - if (strcmp(name, "f_locals") == 0) - PyFrame_FastToLocals(f); - return PyMember_Get((char *)f, frame_memberlist, name); + PyFrame_FastToLocals(f); + Py_INCREF(f->f_locals); + return f->f_locals; } -static int -frame_setattr(PyFrameObject *f, char *name, PyObject *value) -{ - return PyMember_Set((char *)f, frame_memberlist, name, value); -} +static struct getsetlist frame_getsetlist[] = { + {"f_locals", (getter)frame_getlocals, NULL, NULL}, + {0} +}; /* Stack frames are allocated and deallocated at a considerable rate. In an attempt to improve the speed of function calls, we maintain a @@ -177,8 +175,8 @@ PyTypeObject PyFrame_Type = { 0, (destructor)frame_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)frame_getattr, /* tp_getattr */ - (setattrfunc)frame_setattr, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ @@ -187,13 +185,22 @@ PyTypeObject PyFrame_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)frame_traverse, /* tp_traverse */ (inquiry)frame_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + frame_memberlist, /* tp_members */ + frame_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; PyFrameObject * diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 09c6cb80217..311bcdeff99 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "compile.h" +#include "eval.h" #include "structmember.h" PyObject * @@ -141,9 +142,8 @@ static struct memberlist func_memberlist[] = { }; static PyObject * -func_getattro(PyFunctionObject *op, PyObject *name) +func_getattro(PyObject *op, PyObject *name) { - PyObject *rtn; char *sname = PyString_AsString(name); if (sname[0] != '_' && PyEval_GetRestricted()) { @@ -152,25 +152,12 @@ func_getattro(PyFunctionObject *op, PyObject *name) return NULL; } - /* no API for PyMember_HasAttr() */ - rtn = PyMember_Get((char *)op, func_memberlist, sname); - - if (rtn == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - if (op->func_dict != NULL) { - rtn = PyDict_GetItem(op->func_dict, name); - Py_XINCREF(rtn); - } - if (rtn == NULL) - PyErr_SetObject(PyExc_AttributeError, name); - } - return rtn; + return PyObject_GenericGetAttr(op, name); } static int -func_setattro(PyFunctionObject *op, PyObject *name, PyObject *value) +func_setattro(PyObject *op, PyObject *name, PyObject *value) { - int rtn; char *sname = PyString_AsString(name); if (PyEval_GetRestricted()) { @@ -216,31 +203,7 @@ func_setattro(PyFunctionObject *op, PyObject *name, PyObject *value) } } - rtn = PyMember_Set((char *)op, func_memberlist, sname, value); - if (rtn < 0 && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - if (op->func_dict == NULL) { - /* don't create the dict if we're deleting an - * attribute. In that case, we know we'll get an - * AttributeError. - */ - if (value == NULL) { - PyErr_SetString(PyExc_AttributeError, sname); - return -1; - } - op->func_dict = PyDict_New(); - if (op->func_dict == NULL) - return -1; - } - if (value == NULL) - rtn = PyDict_DelItem(op->func_dict, name); - else - rtn = PyDict_SetItem(op->func_dict, name, value); - /* transform KeyError into AttributeError */ - if (rtn < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) - PyErr_SetString(PyExc_AttributeError, sname); - } - return rtn; + return PyObject_GenericSetAttr(op, name, value); } static void @@ -314,31 +277,324 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) return 0; } +static PyObject * +function_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *result; + PyObject *argdefs; + PyObject **d, **k; + int nk, nd; + + argdefs = PyFunction_GET_DEFAULTS(func); + if (argdefs != NULL && PyTuple_Check(argdefs)) { + d = &PyTuple_GET_ITEM((PyTupleObject *)argdefs, 0); + nd = PyTuple_Size(argdefs); + } + else { + d = NULL; + nd = 0; + } + + if (kw != NULL && PyDict_Check(kw)) { + int pos, i; + nk = PyDict_Size(kw); + k = PyMem_NEW(PyObject *, 2*nk); + if (k == NULL) { + PyErr_NoMemory(); + Py_DECREF(arg); + return NULL; + } + pos = i = 0; + while (PyDict_Next(kw, &pos, &k[i], &k[i+1])) + i += 2; + nk = i/2; + /* XXX This is broken if the caller deletes dict items! */ + } + else { + k = NULL; + nk = 0; + } + + result = PyEval_EvalCodeEx( + (PyCodeObject *)PyFunction_GET_CODE(func), + PyFunction_GET_GLOBALS(func), (PyObject *)NULL, + &PyTuple_GET_ITEM(arg, 0), PyTuple_Size(arg), + k, nk, d, nd, + PyFunction_GET_CLOSURE(func)); + + if (k != NULL) + PyMem_DEL(k); + + return result; +} + +/* Bind a function to an object */ +static PyObject * +func_descr_get(PyObject *func, PyObject *obj, PyObject *type) +{ + if (obj == Py_None) + obj = NULL; + return PyMethod_New(func, obj, type); +} + PyTypeObject PyFunction_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "function", sizeof(PyFunctionObject) + PyGC_HEAD_SIZE, 0, - (destructor)func_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)func_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - (getattrofunc)func_getattro, /* tp_getattro */ - (setattrofunc)func_setattro, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)func_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ + (destructor)func_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)func_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + function_call, /* tp_call */ + 0, /* tp_str */ + func_getattro, /* tp_getattro */ + func_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)func_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + func_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + func_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyFunctionObject, func_dict), /* tp_dictoffset */ }; + + +/* Class method object */ + +/* A class method receives the class as implicit first argument, + just like an instance method receives the instance. + To declare a class method, use this idiom: + + class C: + def f(cls, arg1, arg2, ...): ... + f = classmethod(f) + + It can be called either on the class (e.g. C.f()) or on an instance + (e.g. C().f()); the instance is ignored except for its class. + If a class method is called for a derived class, the derived class + object is passed as the implied first argument. + + Class methods are different than C++ or Java static methods. + If you want those, see static methods below. +*/ + +typedef struct { + PyObject_HEAD + PyObject *cm_callable; +} classmethod; + +static void +cm_dealloc(classmethod *cm) +{ + Py_XDECREF(cm->cm_callable); + PyObject_DEL(cm); +} + +static PyObject * +cm_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + classmethod *cm = (classmethod *)self; + + if (cm->cm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized classmethod object"); + return NULL; + } + return PyMethod_New(cm->cm_callable, + type, (PyObject *)(type->ob_type)); +} + +static int +cm_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + classmethod *cm = (classmethod *)self; + PyObject *callable; + + if (!PyArg_ParseTuple(args, "O:callable", &callable)) + return -1; + Py_INCREF(callable); + cm->cm_callable = callable; + return 0; +} + +PyTypeObject PyClassMethod_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "classmethod", + sizeof(classmethod), + 0, + (destructor)cm_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + cm_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + cm_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ +}; + +PyObject * +PyClassMethod_New(PyObject *callable) +{ + classmethod *cm = (classmethod *) + PyType_GenericAlloc(&PyClassMethod_Type, 0); + if (cm != NULL) { + Py_INCREF(callable); + cm->cm_callable = callable; + } + return (PyObject *)cm; +} + + +/* Static method object */ + +/* A static method does not receive an implicit first argument. + To declare a static method, use this idiom: + + class C: + def f(arg1, arg2, ...): ... + f = staticmethod(f) + + It can be called either on the class (e.g. C.f()) or on an instance + (e.g. C().f()); the instance is ignored except for its class. + + Static methods in Python are similar to those found in Java or C++. + For a more advanced concept, see class methods above. +*/ + +typedef struct { + PyObject_HEAD + PyObject *sm_callable; +} staticmethod; + +static void +sm_dealloc(staticmethod *sm) +{ + Py_XDECREF(sm->sm_callable); + PyObject_DEL(sm); +} + +static PyObject * +sm_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + staticmethod *sm = (staticmethod *)self; + + if (sm->sm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized staticmethod object"); + return NULL; + } + Py_INCREF(sm->sm_callable); + return sm->sm_callable; +} + +static int +sm_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + staticmethod *sm = (staticmethod *)self; + PyObject *callable; + + if (!PyArg_ParseTuple(args, "O:callable", &callable)) + return -1; + Py_INCREF(callable); + sm->sm_callable = callable; + return 0; +} + +PyTypeObject PyStaticMethod_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "staticmethod", + sizeof(staticmethod), + 0, + (destructor)sm_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + sm_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + sm_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ +}; + +PyObject * +PyStaticMethod_New(PyObject *callable) +{ + staticmethod *sm = (staticmethod *) + PyType_GenericAlloc(&PyStaticMethod_Type, 0); + if (sm != NULL) { + Py_INCREF(callable); + sm->sm_callable = callable; + } + return (PyObject *)sm; +} diff --git a/Objects/intobject.c b/Objects/intobject.c index 282da201b82..e5106c566ae 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -742,6 +742,41 @@ int_hex(PyIntObject *v) return PyString_FromString(buf); } +static PyObject * +int_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + int base = -909; + static char *kwlist[] = {"x", "base", 0}; + + assert(type == &PyInt_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:int", kwlist, + &x, &base)) + return NULL; + if (x == NULL) + return PyInt_FromLong(0L); + if (base == -909) + return PyNumber_Int(x); + if (PyString_Check(x)) + return PyInt_FromString(PyString_AS_STRING(x), NULL, base); + if (PyUnicode_Check(x)) + return PyInt_FromUnicode(PyUnicode_AS_UNICODE(x), + PyUnicode_GET_SIZE(x), + base); + PyErr_SetString(PyExc_TypeError, + "int() can't convert non-string with explicit base"); + return NULL; +} + +static char int_doc[] = +"int(x[, base]) -> integer\n\ +\n\ +Convert a string or number to an integer, if possible. A floating point\n\ +argument will be truncated towards zero (this does not include a string\n\ +representation of a floating point number!) When converting a string, use\n\ +the optional base. It is an error to supply a base when converting a\n\ +non-string."; + static PyNumberMethods int_as_number = { (binaryfunc)int_add, /*nb_add*/ (binaryfunc)int_sub, /*nb_subtract*/ @@ -785,22 +820,40 @@ PyTypeObject PyInt_Type = { "int", sizeof(PyIntObject), 0, - (destructor)int_dealloc, /*tp_dealloc*/ - (printfunc)int_print, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)int_compare, /*tp_compare*/ - (reprfunc)int_repr, /*tp_repr*/ - &int_as_number, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)int_hash, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_CHECKTYPES /*tp_flags*/ + (destructor)int_dealloc, /* tp_dealloc */ + (printfunc)int_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)int_compare, /* tp_compare */ + (reprfunc)int_repr, /* tp_repr */ + &int_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)int_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + int_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + int_new, /* tp_new */ }; void diff --git a/Objects/iterobject.c b/Objects/iterobject.c index c4f4e616b62..e062c8ab71d 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -96,12 +96,6 @@ static PyMethodDef iter_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -iter_getattr(seqiterobject *it, char *name) -{ - return Py_FindMethod(iter_methods, (PyObject *)it, name); -} - PyTypeObject PySeqIter_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ @@ -111,7 +105,7 @@ PyTypeObject PySeqIter_Type = { /* methods */ (destructor)iter_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)iter_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ @@ -121,7 +115,7 @@ PyTypeObject PySeqIter_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ @@ -132,6 +126,13 @@ PyTypeObject PySeqIter_Type = { 0, /* tp_weaklistoffset */ (getiterfunc)iter_getiter, /* tp_iter */ (iternextfunc)iter_iternext, /* tp_iternext */ + iter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ }; /* -------------------------------------- */ @@ -197,12 +198,6 @@ static PyMethodDef calliter_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -calliter_getattr(calliterobject *it, char *name) -{ - return Py_FindMethod(calliter_methods, (PyObject *)it, name); -} - static PyObject * calliter_iternext(calliterobject *it) { @@ -228,7 +223,7 @@ PyTypeObject PyCallIter_Type = { /* methods */ (destructor)calliter_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)calliter_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ @@ -238,7 +233,7 @@ PyTypeObject PyCallIter_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ @@ -249,4 +244,11 @@ PyTypeObject PyCallIter_Type = { 0, /* tp_weaklistoffset */ (getiterfunc)iter_getiter, /* tp_iter */ (iternextfunc)calliter_iternext, /* tp_iternext */ + calliter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ }; diff --git a/Objects/listobject.c b/Objects/listobject.c index 7166ced8cb4..b77cc0af027 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -523,6 +523,10 @@ list_ass_slice(PyListObject *a, int ilow, int ihigh, PyObject *v) Py_XDECREF(*p); PyMem_DEL(recycle); } + if (a->ob_size == 0 && a->ob_item != NULL) { + PyMem_FREE(a->ob_item); + a->ob_item = NULL; + } return 0; #undef b } @@ -1289,16 +1293,18 @@ listsort(PyListObject *self, PyObject *args) { int err; PyObject *compare = NULL; + PyTypeObject *savetype; if (args != NULL) { if (!PyArg_ParseTuple(args, "|O:sort", &compare)) return NULL; } + savetype = self->ob_type; self->ob_type = &immutable_list_type; err = samplesortslice(self->ob_item, self->ob_item + self->ob_size, compare); - self->ob_type = &PyList_Type; + self->ob_type = savetype; if (err < 0) return NULL; Py_INCREF(Py_None); @@ -1541,6 +1547,100 @@ list_richcompare(PyObject *v, PyObject *w, int op) return PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op); } +/* Adapted from newer code by Tim */ +static int +list_fill(PyListObject *result, PyObject *v) +{ + PyObject *it; /* iter(v) */ + int n; /* guess for result list size */ + int i; + + n = result->ob_size; + + /* Special-case list(a_list), for speed. */ + if (PyList_Check(v)) { + if (v == (PyObject *)result) + return 0; /* source is destination, we're done */ + return list_ass_slice(result, 0, n, v); + } + + /* Empty previous contents */ + if (n != 0) { + if (list_ass_slice(result, 0, n, (PyObject *)NULL) != 0) + return -1; + } + + /* Get iterator. There may be some low-level efficiency to be gained + * by caching the tp_iternext slot instead of using PyIter_Next() + * later, but premature optimization is the root etc. + */ + it = PyObject_GetIter(v); + if (it == NULL) + return -1; + + /* Guess a result list size. */ + n = -1; /* unknown */ + if (PySequence_Check(v) && + v->ob_type->tp_as_sequence->sq_length) { + n = PySequence_Size(v); + if (n < 0) + PyErr_Clear(); + } + if (n < 0) + n = 8; /* arbitrary */ + NRESIZE(result->ob_item, PyObject*, n); + if (result->ob_item == NULL) + goto error; + for (i = 0; i < n; i++) + result->ob_item[i] = NULL; + result->ob_size = n; + + /* Run iterator to exhaustion. */ + for (i = 0; ; i++) { + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto error; + break; + } + if (i < n) + PyList_SET_ITEM(result, i, item); /* steals ref */ + else { + int status = ins1(result, result->ob_size, item); + Py_DECREF(item); /* append creates a new ref */ + if (status < 0) + goto error; + } + } + + /* Cut back result list if initial guess was too large. */ + if (i < n && result != NULL) { + if (list_ass_slice(result, i, n, (PyObject *)NULL) != 0) + goto error; + } + Py_DECREF(it); + return 0; + + error: + Py_DECREF(it); + return -1; +} + +static int +list_init(PyListObject *self, PyObject *args, PyObject *kw) +{ + PyObject *arg = NULL; + static char *kwlist[] = {"sequence", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:list", kwlist, &arg)) + return -1; + if (arg != NULL) + return list_fill(self, arg); + if (self->ob_size > 0) + return list_ass_slice(self, 0, self->ob_size, (PyObject*)NULL); + return 0; +} + static char append_doc[] = "L.append(object) -- append object to end"; static char extend_doc[] = @@ -1573,12 +1673,6 @@ static PyMethodDef list_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -list_getattr(PyListObject *f, char *name) -{ - return Py_FindMethod(list_methods, (PyObject *)f, name); -} - static PySequenceMethods list_as_sequence = { (inquiry)list_length, /* sq_length */ (binaryfunc)list_concat, /* sq_concat */ @@ -1592,6 +1686,10 @@ static PySequenceMethods list_as_sequence = { (intargfunc)list_inplace_repeat, /* sq_inplace_repeat */ }; +static char list_doc[] = +"list() -> new list\n" +"list(sequence) -> new list initialized from sequence's items"; + PyTypeObject PyList_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -1600,7 +1698,7 @@ PyTypeObject PyList_Type = { 0, (destructor)list_dealloc, /* tp_dealloc */ (printfunc)list_print, /* tp_print */ - (getattrfunc)list_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)list_repr, /* tp_repr */ @@ -1610,14 +1708,29 @@ PyTypeObject PyList_Type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ - 0, /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + list_doc, /* tp_doc */ (traverseproc)list_traverse, /* tp_traverse */ (inquiry)list_clear, /* tp_clear */ list_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + list_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)list_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ }; @@ -1646,12 +1759,6 @@ static PyMethodDef immutable_list_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -immutable_list_getattr(PyListObject *f, char *name) -{ - return Py_FindMethod(immutable_list_methods, (PyObject *)f, name); -} - static int immutable_list_ass(void) { @@ -1678,7 +1785,7 @@ static PyTypeObject immutable_list_type = { 0, 0, /* Cannot happen */ /* tp_dealloc */ (printfunc)list_print, /* tp_print */ - (getattrfunc)immutable_list_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* Won't be called */ /* tp_compare */ (reprfunc)list_repr, /* tp_repr */ @@ -1688,13 +1795,24 @@ static PyTypeObject immutable_list_type = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ - 0, /* tp_doc */ + list_doc, /* tp_doc */ (traverseproc)list_traverse, /* tp_traverse */ 0, /* tp_clear */ list_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + immutable_list_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_init */ /* NOTE: This is *not* the standard list_type struct! */ }; diff --git a/Objects/longobject.c b/Objects/longobject.c index 71bd1ce2e4c..28c3e577938 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -2031,6 +2031,43 @@ long_hex(PyObject *v) return long_format(v, 16, 1); } +static PyObject * +long_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + int base = -909; /* unlikely! */ + static char *kwlist[] = {"x", "base", 0}; + + assert(type == &PyLong_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:long", kwlist, + &x, &base)) + return NULL; + if (x == NULL) + return PyLong_FromLong(0L); + if (base == -909) + return PyNumber_Long(x); + else if (PyString_Check(x)) + return PyLong_FromString(PyString_AS_STRING(x), NULL, base); + else if (PyUnicode_Check(x)) + return PyLong_FromUnicode(PyUnicode_AS_UNICODE(x), + PyUnicode_GET_SIZE(x), + base); + else { + PyErr_SetString(PyExc_TypeError, + "long() can't convert non-string with explicit base"); + return NULL; + } +} + +static char long_doc[] = +"long(x[, base]) -> integer\n\ +\n\ +Convert a string or number to a long integer, if possible. A floating\n\ +point argument will be truncated towards zero (this does not include a\n\ +string representation of a floating point number!) When converting a\n\ +string, use the optional base. It is an error to supply a base when\n\ +converting a non-string."; + static PyNumberMethods long_as_number = { (binaryfunc) long_add, /*nb_add*/ (binaryfunc) long_sub, /*nb_subtract*/ @@ -2070,24 +2107,42 @@ static PyNumberMethods long_as_number = { PyTypeObject PyLong_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, - "long int", - sizeof(PyLongObject) - sizeof(digit), - sizeof(digit), - (destructor)long_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)long_compare, /*tp_compare*/ - (reprfunc)long_repr, /*tp_repr*/ - &long_as_number, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)long_hash, /*tp_hash*/ - 0, /*tp_call*/ - (reprfunc)long_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_CHECKTYPES /*tp_flags*/ + 0, /* ob_size */ + "long", /* tp_name */ + sizeof(PyLongObject) - sizeof(digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ + (destructor)long_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)long_compare, /* tp_compare */ + (reprfunc)long_repr, /* tp_repr */ + &long_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)long_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)long_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + long_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + long_new, /* tp_new */ }; diff --git a/Objects/methodobject.c b/Objects/methodobject.c index bff79ed042d..56fbcc22fd9 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -3,8 +3,6 @@ #include "Python.h" -#include "token.h" - static PyCFunctionObject *free_list = NULL; PyObject * @@ -69,6 +67,23 @@ meth_dealloc(PyCFunctionObject *m) free_list = m; } +static PyObject * +meth_get__doc__(PyCFunctionObject *m, void *closure) +{ + char *doc = m->m_ml->ml_doc; + + if (doc != NULL) + return PyString_FromString(doc); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +meth_get__name__(PyCFunctionObject *m, void *closure) +{ + return PyString_FromString(m->m_ml->ml_name); +} + static int meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) { @@ -79,39 +94,28 @@ meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) } static PyObject * -meth_getattr(PyCFunctionObject *m, char *name) +meth_get__self__(PyCFunctionObject *m, void *closure) { - if (strcmp(name, "__name__") == 0) { - return PyString_FromString(m->m_ml->ml_name); + PyObject *self; + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "method.__self__ not accessible in restricted mode"); + return NULL; } - if (strcmp(name, "__doc__") == 0) { - char *doc = m->m_ml->ml_doc; - if (doc != NULL) - return PyString_FromString(doc); - Py_INCREF(Py_None); - return Py_None; - } - if (strcmp(name, "__self__") == 0) { - PyObject *self; - if (PyEval_GetRestricted()) { - PyErr_SetString(PyExc_RuntimeError, - "method.__self__ not accessible in restricted mode"); - return NULL; - } - self = m->m_self; - if (self == NULL) - self = Py_None; - Py_INCREF(self); - return self; - } - if (strcmp(name, "__members__") == 0) { - return Py_BuildValue("[sss]", - "__doc__", "__name__", "__self__"); - } - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + self = m->m_self; + if (self == NULL) + self = Py_None; + Py_INCREF(self); + return self; } +static struct getsetlist meth_getsets [] = { + {"__doc__", (getter)meth_get__doc__, NULL, NULL}, + {"__name__", (getter)meth_get__name__, NULL, NULL}, + {"__self__", (getter)meth_get__self__, NULL, NULL}, + {0} +}; + static PyObject * meth_repr(PyCFunctionObject *m) { @@ -159,6 +163,41 @@ meth_hash(PyCFunctionObject *a) return x; } +static PyObject * +meth_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyCFunctionObject* f = (PyCFunctionObject*)func; + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + int flags = PyCFunction_GET_FLAGS(func); + + if (flags & METH_KEYWORDS) { + return (*(PyCFunctionWithKeywords)meth)(self, arg, kw); + } + if (kw != NULL && PyDict_Size(kw) != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + f->m_ml->ml_name); + return NULL; + } + if (flags & METH_VARARGS) { + return (*meth)(self, arg); + } + if (!(flags & METH_VARARGS)) { + /* the really old style */ + int size = PyTuple_GET_SIZE(arg); + if (size == 1) + arg = PyTuple_GET_ITEM(arg, 0); + else if (size == 0) + arg = NULL; + return (*meth)(self, arg); + } + /* should never get here ??? */ + PyErr_BadInternalCall(); + return NULL; +} + + PyTypeObject PyCFunction_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -167,7 +206,7 @@ PyTypeObject PyCFunction_Type = { 0, (destructor)meth_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)meth_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)meth_compare, /* tp_compare */ (reprfunc)meth_repr, /* tp_repr */ @@ -175,14 +214,24 @@ PyTypeObject PyCFunction_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)meth_hash, /* tp_hash */ - 0, /* tp_call */ + meth_call, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)meth_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + meth_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; /* List all methods in a chain -- helper for findmethodinchain */ diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 7df5ce0b1ba..7faa3bbfd3e 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -2,12 +2,18 @@ /* Module object implementation */ #include "Python.h" +#include "structmember.h" typedef struct { PyObject_HEAD PyObject *md_dict; } PyModuleObject; +struct memberlist module_members[] = { + {"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY}, + {0} +}; + PyObject * PyModule_New(char *name) { @@ -128,6 +134,15 @@ _PyModule_Clear(PyObject *m) /* Methods */ +static int +module_init(PyModuleObject *m, PyObject *args, PyObject *kw) +{ + m->md_dict = PyDict_New(); + if (m->md_dict == NULL) + return -1; + return 0; +} + static void module_dealloc(PyModuleObject *m) { @@ -161,59 +176,6 @@ module_repr(PyModuleObject *m) return PyString_FromString(buf); } -static PyObject * -module_getattro(PyModuleObject *m, PyObject *name) -{ - PyObject *res; - char *sname = PyString_AsString(name); - - if (sname[0] == '_' && strcmp(sname, "__dict__") == 0) { - Py_INCREF(m->md_dict); - return m->md_dict; - } - res = PyDict_GetItem(m->md_dict, name); - if (res == NULL) { - char *modname = PyModule_GetName((PyObject *)m); - if (modname == NULL) { - PyErr_Clear(); - modname = "?"; - } - PyErr_Format(PyExc_AttributeError, - "'%.50s' module has no attribute '%.400s'", - modname, sname); - } - else - Py_INCREF(res); - return res; -} - -static int -module_setattro(PyModuleObject *m, PyObject *name, PyObject *v) -{ - char *sname = PyString_AsString(name); - if (sname[0] == '_' && strcmp(sname, "__dict__") == 0) { - PyErr_SetString(PyExc_TypeError, - "read-only special attribute"); - return -1; - } - if (v == NULL) { - int rv = PyDict_DelItem(m->md_dict, name); - if (rv < 0) { - char *modname = PyModule_GetName((PyObject *)m); - if (modname == NULL) { - PyErr_Clear(); - modname = "?"; - } - PyErr_Format(PyExc_AttributeError, - "'%.50s' module has no attribute '%.400s'", - modname, sname); - } - return rv; - } - else - return PyDict_SetItem(m->md_dict, name, v); -} - /* We only need a traverse function, no clear function: If the module is in a cycle, md_dict will be cleared as well, which will break the cycle. */ @@ -229,24 +191,41 @@ PyTypeObject PyModule_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "module", /* tp_name */ - sizeof(PyModuleObject) + PyGC_HEAD_SIZE,/* tp_size */ + sizeof(PyModuleObject) + PyGC_HEAD_SIZE, /* tp_size */ 0, /* tp_itemsize */ - (destructor)module_dealloc, /* tp_dealloc */ + (destructor)module_dealloc, /* tp_dealloc */ 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /* tp_compare */ - (reprfunc)module_repr, /* tp_repr */ + (reprfunc)module_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - (getattrofunc)module_getattro, /* tp_getattro */ - (setattrofunc)module_setattro, /* tp_setattro */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ 0, /* tp_doc */ (traverseproc)module_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + module_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyModuleObject, md_dict), /* tp_dictoffset */ + (initproc)module_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ }; diff --git a/Objects/object.c b/Objects/object.c index 87c8e1a801d..137752d841d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -32,7 +32,7 @@ dump_counts(void) for (tp = type_list; tp; tp = tp->tp_next) fprintf(stderr, "%s alloc'd: %d, freed: %d, max in use: %d\n", - tp->tp_name, tp->tp_alloc, tp->tp_free, + tp->tp_name, tp->tp_allocs, tp->tp_frees, tp->tp_maxalloc); fprintf(stderr, "fast tuple allocs: %d, empty: %d\n", fast_tuple_allocs, tuple_zero_allocs); @@ -53,8 +53,8 @@ get_counts(void) if (result == NULL) return NULL; for (tp = type_list; tp; tp = tp->tp_next) { - v = Py_BuildValue("(siii)", tp->tp_name, tp->tp_alloc, - tp->tp_free, tp->tp_maxalloc); + v = Py_BuildValue("(siii)", tp->tp_name, tp->tp_allocs, + tp->tp_frees, tp->tp_maxalloc); if (v == NULL) { Py_DECREF(result); return NULL; @@ -72,16 +72,16 @@ get_counts(void) void inc_count(PyTypeObject *tp) { - if (tp->tp_alloc == 0) { + if (tp->tp_allocs == 0) { /* first time; insert in linked list */ if (tp->tp_next != NULL) /* sanity check */ Py_FatalError("XXX inc_count sanity check"); tp->tp_next = type_list; type_list = tp; } - tp->tp_alloc++; - if (tp->tp_alloc - tp->tp_free > tp->tp_maxalloc) - tp->tp_maxalloc = tp->tp_alloc - tp->tp_free; + tp->tp_allocs++; + if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc) + tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees; } #endif @@ -93,10 +93,8 @@ PyObject_Init(PyObject *op, PyTypeObject *tp) "NULL object passed to PyObject_Init"); return op; } -#ifdef WITH_CYCLE_GC if (PyType_IS_GC(tp)) op = (PyObject *) PyObject_FROM_GC(op); -#endif /* Any changes should be reflected in PyObject_INIT (objimpl.h) */ op->ob_type = tp; _Py_NewReference(op); @@ -111,10 +109,8 @@ PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, int size) "NULL object passed to PyObject_InitVar"); return op; } -#ifdef WITH_CYCLE_GC if (PyType_IS_GC(tp)) op = (PyVarObject *) PyObject_FROM_GC(op); -#endif /* Any changes should be reflected in PyObject_INIT_VAR */ op->ob_size = size; op->ob_type = tp; @@ -129,10 +125,8 @@ _PyObject_New(PyTypeObject *tp) op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp)); if (op == NULL) return PyErr_NoMemory(); -#ifdef WITH_CYCLE_GC if (PyType_IS_GC(tp)) op = (PyObject *) PyObject_FROM_GC(op); -#endif return PyObject_INIT(op, tp); } @@ -143,21 +137,17 @@ _PyObject_NewVar(PyTypeObject *tp, int size) op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size)); if (op == NULL) return (PyVarObject *)PyErr_NoMemory(); -#ifdef WITH_CYCLE_GC if (PyType_IS_GC(tp)) op = (PyVarObject *) PyObject_FROM_GC(op); -#endif return PyObject_INIT_VAR(op, tp, size); } void _PyObject_Del(PyObject *op) { -#ifdef WITH_CYCLE_GC if (op && PyType_IS_GC(op->ob_type)) { op = (PyObject *) PyObject_AS_GC(op); } -#endif PyObject_FREE(op); } @@ -994,26 +984,16 @@ PyObject_Hash(PyObject *v) PyObject * PyObject_GetAttrString(PyObject *v, char *name) { - if (v->ob_type->tp_getattro != NULL) { - PyObject *w, *res; - w = PyString_InternFromString(name); - if (w == NULL) - return NULL; - res = (*v->ob_type->tp_getattro)(v, w); - Py_XDECREF(w); - return res; - } + PyObject *w, *res; - if (v->ob_type->tp_getattr == NULL) { - PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%.400s'", - v->ob_type->tp_name, - name); - return NULL; - } - else { + if (v->ob_type->tp_getattr != NULL) return (*v->ob_type->tp_getattr)(v, name); - } + w = PyString_InternFromString(name); + if (w == NULL) + return NULL; + res = PyObject_GetAttr(v, w); + Py_XDECREF(w); + return res; } int @@ -1031,34 +1011,24 @@ PyObject_HasAttrString(PyObject *v, char *name) int PyObject_SetAttrString(PyObject *v, char *name, PyObject *w) { - if (v->ob_type->tp_setattro != NULL) { - PyObject *s; - int res; - s = PyString_InternFromString(name); - if (s == NULL) - return -1; - res = (*v->ob_type->tp_setattro)(v, s, w); - Py_XDECREF(s); - return res; - } + PyObject *s; + int res; - if (v->ob_type->tp_setattr == NULL) { - if (v->ob_type->tp_getattr == NULL) - PyErr_SetString(PyExc_TypeError, - "attribute-less object (assign or del)"); - else - PyErr_SetString(PyExc_TypeError, - "object has read-only attributes"); - return -1; - } - else { + if (v->ob_type->tp_setattr != NULL) return (*v->ob_type->tp_setattr)(v, name, w); - } + s = PyString_InternFromString(name); + if (s == NULL) + return -1; + res = PyObject_SetAttr(v, s, w); + Py_XDECREF(s); + return res; } PyObject * PyObject_GetAttr(PyObject *v, PyObject *name) { + PyTypeObject *tp = v->ob_type; + /* The Unicode to string conversion is done here because the existing tp_getattro slots expect a string object as name and we wouldn't want to break those. */ @@ -1067,16 +1037,19 @@ PyObject_GetAttr(PyObject *v, PyObject *name) if (name == NULL) return NULL; } - if (!PyString_Check(name)) { PyErr_SetString(PyExc_TypeError, "attribute name must be string"); return NULL; } - if (v->ob_type->tp_getattro != NULL) - return (*v->ob_type->tp_getattro)(v, name); - else - return PyObject_GetAttrString(v, PyString_AS_STRING(name)); + if (tp->tp_getattro != NULL) + return (*tp->tp_getattro)(v, name); + if (tp->tp_getattr != NULL) + return (*tp->tp_getattr)(v, PyString_AS_STRING(name)); + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + return NULL; } int @@ -1094,6 +1067,7 @@ PyObject_HasAttr(PyObject *v, PyObject *name) int PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value) { + PyTypeObject *tp = v->ob_type; int err; /* The Unicode to string conversion is done here because the @@ -1104,25 +1078,182 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value) if (name == NULL) return -1; } - else - Py_INCREF(name); - - if (!PyString_Check(name)){ + else if (!PyString_Check(name)){ PyErr_SetString(PyExc_TypeError, "attribute name must be string"); - err = -1; + return -1; } - else { - PyString_InternInPlace(&name); - if (v->ob_type->tp_setattro != NULL) - err = (*v->ob_type->tp_setattro)(v, name, value); - else - err = PyObject_SetAttrString(v, - PyString_AS_STRING(name), value); + else + Py_INCREF(name); + + PyString_InternInPlace(&name); + if (tp->tp_setattro != NULL) { + err = (*tp->tp_setattro)(v, name, value); + Py_DECREF(name); + return err; + } + if (tp->tp_setattr != NULL) { + err = (*tp->tp_setattr)(v, PyString_AS_STRING(name), value); + Py_DECREF(name); + return err; } - Py_DECREF(name); - return err; + if (tp->tp_getattr == NULL && tp->tp_getattro == NULL) + PyErr_Format(PyExc_TypeError, + "'%.100s' object has no attributes " + "(%s .%.100s)", + tp->tp_name, + value==NULL ? "del" : "assign to", + PyString_AS_STRING(name)); + else + PyErr_Format(PyExc_TypeError, + "'%.100s' object has only read-only attributes " + "(%s .%.100s)", + tp->tp_name, + value==NULL ? "del" : "assign to", + PyString_AS_STRING(name)); + return -1; +} + +/* Helper to get a pointer to an object's __dict__ slot, if any */ + +PyObject ** +_PyObject_GetDictPtr(PyObject *obj) +{ +#define PTRSIZE (sizeof(PyObject *)) + + long dictoffset; + PyTypeObject *tp = obj->ob_type; + + if (!(tp->tp_flags & Py_TPFLAGS_HAVE_CLASS)) + return NULL; + dictoffset = tp->tp_dictoffset; + if (dictoffset == 0) + return NULL; + if (dictoffset < 0) { + dictoffset += PyType_BASICSIZE(tp); + assert(dictoffset > 0); /* Sanity check */ + if (tp->tp_itemsize > 0) { + int n = ((PyVarObject *)obj)->ob_size; + if (n > 0) { + dictoffset += tp->tp_itemsize * n; + /* Round up, if necessary */ + if (tp->tp_itemsize % PTRSIZE != 0) { + dictoffset += PTRSIZE - 1; + dictoffset /= PTRSIZE; + dictoffset *= PTRSIZE; + } + } + } + } + return (PyObject **) ((char *)obj + dictoffset); +} + +/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */ + +PyObject * +PyObject_GenericGetAttr(PyObject *obj, PyObject *name) +{ + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + descrgetfunc f; + PyObject **dictptr; + + if (tp->tp_dict == NULL) { + if (PyType_InitDict(tp) < 0) + return NULL; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL) { + f = descr->ob_type->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) + return f(descr, obj, (PyObject *)obj->ob_type); + } + + dictptr = _PyObject_GetDictPtr(obj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict != NULL) { + PyObject *res = PyDict_GetItem(dict, name); + if (res != NULL) { + Py_INCREF(res); + return res; + } + } + } + + if (f != NULL) + return f(descr, obj, (PyObject *)obj->ob_type); + + if (descr != NULL) { + Py_INCREF(descr); + return descr; + } + + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + return NULL; +} + +int +PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) +{ + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + descrsetfunc f; + PyObject **dictptr; + + if (tp->tp_dict == NULL) { + if (PyType_InitDict(tp) < 0) + return -1; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL) { + f = descr->ob_type->tp_descr_set; + if (f != NULL && PyDescr_IsData(descr)) + return f(descr, obj, value); + } + + dictptr = _PyObject_GetDictPtr(obj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL && value != NULL) { + dict = PyDict_New(); + if (dict == NULL) + return -1; + *dictptr = dict; + } + if (dict != NULL) { + int res; + if (value == NULL) + res = PyDict_DelItem(dict, name); + else + res = PyDict_SetItem(dict, name, value); + if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) + PyErr_SetObject(PyExc_AttributeError, name); + return res; + } + } + + if (f != NULL) + return f(descr, obj, value); + + if (descr == NULL) { + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + return -1; + } + + PyErr_Format(PyExc_AttributeError, + "'%.50s' object attribute '%.400s' is read-only", + tp->tp_name, PyString_AS_STRING(name)); + return -1; } /* Test a value used as condition, e.g., in a for or if statement. @@ -1218,12 +1349,6 @@ PyCallable_Check(PyObject *x) { if (x == NULL) return 0; - if (x->ob_type->tp_call != NULL || - PyFunction_Check(x) || - PyMethod_Check(x) || - PyCFunction_Check(x) || - PyClass_Check(x)) - return 1; if (PyInstance_Check(x)) { PyObject *call = PyObject_GetAttrString(x, "__call__"); if (call == NULL) { @@ -1235,7 +1360,9 @@ PyCallable_Check(PyObject *x) Py_DECREF(call); return 1; } - return 0; + else { + return x->ob_type->tp_call != NULL; + } } @@ -1365,7 +1492,7 @@ _Py_ForgetReference(register PyObject *op) op->_ob_prev->_ob_next = op->_ob_next; op->_ob_next = op->_ob_prev = NULL; #ifdef COUNT_ALLOCS - op->ob_type->tp_free++; + op->ob_type->tp_frees++; #endif } diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 1f446df2dd4..9f155cf5998 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -310,22 +310,34 @@ PyTypeObject PyRange_Type = { "xrange", /* Name of this type */ sizeof(rangeobject), /* Basic object size */ 0, /* Item size for varobject */ - (destructor)range_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)range_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)range_compare, /*tp_compare*/ - (reprfunc)range_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - &range_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ + (destructor)range_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)range_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + (cmpfunc)range_compare, /*tp_compare*/ + (reprfunc)range_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + &range_as_sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; #undef WARN diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 6f7a6d818ba..8f52f9ec172 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -14,6 +14,7 @@ this type and there is exactly one in existence. */ #include "Python.h" +#include "structmember.h" static PyObject * ellipsis_repr(PyObject *op) @@ -128,32 +129,12 @@ slice_repr(PySliceObject *r) return s; } - -static PyObject *slice_getattr(PySliceObject *self, char *name) -{ - PyObject *ret; - - ret = NULL; - if (strcmp(name, "start") == 0) { - ret = self->start; - } - else if (strcmp(name, "stop") == 0) { - ret = self->stop; - } - else if (strcmp(name, "step") == 0) { - ret = self->step; - } - else if (strcmp(name, "__members__") == 0) { - return Py_BuildValue("[sss]", - "start", "stop", "step"); - } - else { - PyErr_SetString(PyExc_AttributeError, name); - return NULL; - } - Py_INCREF(ret); - return ret; -} +static struct memberlist slice_members[] = { + {"start", T_OBJECT, offsetof(PySliceObject, start), READONLY}, + {"stop", T_OBJECT, offsetof(PySliceObject, stop), READONLY}, + {"step", T_OBJECT, offsetof(PySliceObject, step), READONLY}, + {0} +}; static int slice_compare(PySliceObject *v, PySliceObject *w) @@ -182,13 +163,32 @@ PyTypeObject PySlice_Type = { "slice", /* Name of this type */ sizeof(PySliceObject), /* Basic object size */ 0, /* Item size for varobject */ - (destructor)slice_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)slice_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)slice_compare, /*tp_compare*/ - (reprfunc)slice_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + (destructor)slice_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)slice_compare, /* tp_compare */ + (reprfunc)slice_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + slice_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; diff --git a/Objects/stringobject.c b/Objects/stringobject.c index c7f5062a0fb..3d12588d2c6 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -2522,41 +2522,65 @@ string_methods[] = { }; static PyObject * -string_getattr(PyStringObject *s, char *name) +string_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - return Py_FindMethod(string_methods, (PyObject*)s, name); + PyObject *x = NULL; + static char *kwlist[] = {"object", 0}; + + assert(type == &PyString_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:str", kwlist, &x)) + return NULL; + if (x == NULL) + return PyString_FromString(""); + return PyObject_Str(x); } +static char string_doc[] = +"str(object) -> string\n\ +\n\ +Return a nice string representation of the object.\n\ +If the argument is a string, the return value is the same object."; PyTypeObject PyString_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, - "string", + "str", sizeof(PyStringObject), sizeof(char), - (destructor)string_dealloc, /*tp_dealloc*/ - (printfunc)string_print, /*tp_print*/ - (getattrfunc)string_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)string_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - &string_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)string_hash, /*tp_hash*/ - 0, /*tp_call*/ - (reprfunc)string_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - &string_as_buffer, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - (richcmpfunc)string_richcompare, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ + (destructor)string_dealloc, /* tp_dealloc */ + (printfunc)string_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)string_repr, /* tp_repr */ + 0, /* tp_as_number */ + &string_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)string_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)string_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &string_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + string_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)string_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + string_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + string_new, /* tp_new */ }; void diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 538cc704d20..46f5714364b 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -480,6 +480,28 @@ tuplerichcompare(PyObject *v, PyObject *w, int op) return PyObject_RichCompare(vt->ob_item[i], wt->ob_item[i], op); } +static PyObject * +tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *arg = NULL; + static char *kwlist[] = {"sequence", 0}; + + assert(type == &PyTuple_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:tuple", kwlist, &arg)) + return NULL; + + if (arg == NULL) + return PyTuple_New(0); + else + return PySequence_Tuple(arg); +} + +static char tuple_doc[] = +"tuple(sequence) -> list\n\ +\n\ +Return a tuple whose items are the same as those of the argument sequence.\n\ +If the argument is a tuple, the return value is the same object."; + static PySequenceMethods tuple_as_sequence = { (inquiry)tuplelength, /* sq_length */ (binaryfunc)tupleconcat, /* sq_concat */ @@ -509,14 +531,28 @@ PyTypeObject PyTuple_Type = { (hashfunc)tuplehash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ - 0, /* tp_doc */ + tuple_doc, /* tp_doc */ (traverseproc)tupletraverse, /* tp_traverse */ 0, /* tp_clear */ tuplerichcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + tuple_new, /* tp_new */ }; /* The following function breaks the notion that tuples are immutable: diff --git a/Objects/typeobject.c b/Objects/typeobject.c index ea3494eed2c..429680d148e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2,27 +2,78 @@ /* Type object implementation */ #include "Python.h" +#include "structmember.h" -/* Type object implementation */ +staticforward int add_members(PyTypeObject *, struct memberlist *); + +static struct memberlist type_members[] = { + {"__name__", T_STRING, offsetof(PyTypeObject, tp_name), READONLY}, + {"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY}, + {"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY}, + {"__flags__", T_LONG, offsetof(PyTypeObject, tp_flags), READONLY}, + {"__doc__", T_STRING, offsetof(PyTypeObject, tp_doc), READONLY}, + {"__weaklistoffset__", T_LONG, + offsetof(PyTypeObject, tp_weaklistoffset), READONLY}, + {"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY}, + {"__dictoffset__", T_LONG, + offsetof(PyTypeObject, tp_dictoffset), READONLY}, + {"__bases__", T_OBJECT, offsetof(PyTypeObject, tp_bases), READONLY}, + {"__mro__", T_OBJECT, offsetof(PyTypeObject, tp_mro), READONLY}, + {0} +}; static PyObject * -type_getattr(PyTypeObject *t, char *name) +type_module(PyTypeObject *type, void *context) { - if (strcmp(name, "__name__") == 0) - return PyString_FromString(t->tp_name); - if (strcmp(name, "__doc__") == 0) { - char *doc = t->tp_doc; - if (doc != NULL) - return PyString_FromString(doc); + return PyString_FromString("__builtin__"); +} + +static PyObject * +type_dict(PyTypeObject *type, void *context) +{ + if (type->tp_dict == NULL) { Py_INCREF(Py_None); return Py_None; } - if (strcmp(name, "__members__") == 0) - return Py_BuildValue("[ss]", "__doc__", "__name__"); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) { + Py_INCREF(type->tp_dict); + return type->tp_dict; + } + return PyDictProxy_New(type->tp_dict); } +static PyObject * +type_defined(PyTypeObject *type, void *context) +{ + if (type->tp_defined == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) { + Py_INCREF(type->tp_defined); + return type->tp_defined; + } + return PyDictProxy_New(type->tp_defined); +} + +static PyObject * +type_dynamic(PyTypeObject *type, void *context) +{ + PyObject *res; + + res = (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) ? Py_True : Py_False; + Py_INCREF(res); + return res; +} + +struct getsetlist type_getsets[] = { + {"__module__", (getter)type_module, NULL, NULL}, + {"__dict__", (getter)type_dict, NULL, NULL}, + {"__defined__", (getter)type_defined, NULL, NULL}, + {"__dynamic__", (getter)type_dynamic, NULL, NULL}, + {0} +}; + static int type_compare(PyObject *v, PyObject *w) { @@ -34,34 +85,2271 @@ type_compare(PyObject *v, PyObject *w) } static PyObject * -type_repr(PyTypeObject *v) +type_repr(PyTypeObject *type) { char buf[100]; - sprintf(buf, "", v->tp_name); + sprintf(buf, "", type->tp_name); return PyString_FromString(buf); } +static PyObject * +type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj; + + if (type->tp_new == NULL) { + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances", + type->tp_name); + return NULL; + } + + obj = type->tp_new(type, args, NULL); + if (obj != NULL) { + type = obj->ob_type; + if (type->tp_init != NULL && + type->tp_init(obj, args, kwds) < 0) { + Py_DECREF(obj); + obj = NULL; + } + } + return obj; +} + +PyObject * +PyType_GenericAlloc(PyTypeObject *type, int nitems) +{ + int size; + void *mem; + PyObject *obj; + + /* Inline PyObject_New() so we can zero the memory */ + size = _PyObject_VAR_SIZE(type, nitems); + mem = PyObject_MALLOC(size); + if (mem == NULL) + return PyErr_NoMemory(); + memset(mem, '\0', size); + if (PyType_IS_GC(type)) + obj = PyObject_FROM_GC(mem); + else + obj = (PyObject *)mem; + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_INCREF(type); + if (type->tp_itemsize == 0) + PyObject_INIT(obj, type); + else + (void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems); + if (PyType_IS_GC(type)) + PyObject_GC_Init(obj); + return obj; +} + +PyObject * +PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return type->tp_alloc(type, 0); +} + +/* Helper for subtyping */ + +static void +subtype_dealloc(PyObject *self) +{ + int dictoffset = self->ob_type->tp_dictoffset; + PyTypeObject *type, *base; + destructor f; + + /* This exists so we can DECREF self->ob_type */ + + /* Find the nearest base with a different tp_dealloc */ + type = self->ob_type; + base = type->tp_base; + while ((f = base->tp_dealloc) == subtype_dealloc) { + base = base->tp_base; + assert(base); + } + + /* If we added a dict, DECREF it */ + if (dictoffset && !base->tp_dictoffset) { + PyObject **dictptr = (PyObject **) ((char *)self + dictoffset); + PyObject *dict = *dictptr; + if (dict != NULL) { + Py_DECREF(dict); + *dictptr = NULL; + } + } + + /* Finalize GC if the base doesn't do GC and we do */ + if (PyType_IS_GC(type) && !PyType_IS_GC(base)) + PyObject_GC_Fini(self); + + /* Call the base tp_dealloc() */ + assert(f); + f(self); + + /* Can't reference self beyond this point */ + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + Py_DECREF(type); + } +} + +staticforward void override_slots(PyTypeObject *type, PyObject *dict); +staticforward PyTypeObject *solid_base(PyTypeObject *type); + +typedef struct { + PyTypeObject type; + PyNumberMethods as_number; + PySequenceMethods as_sequence; + PyMappingMethods as_mapping; + PyBufferProcs as_buffer; + PyObject *name, *slots; + struct memberlist members[1]; +} etype; + +/* type test with subclassing support */ + +int +PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) +{ + PyObject *mro; + + mro = a->tp_mro; + if (mro != NULL) { + /* Deal with multiple inheritance without recursion + by walking the MRO tuple */ + int i, n; + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) + return 1; + } + return 0; + } + else { + /* a is not completely initilized yet; follow tp_base */ + do { + if (a == b) + return 1; + a = a->tp_base; + } while (a != NULL); + return b == &PyBaseObject_Type; + } +} + +/* Method resolution order algorithm from "Putting Metaclasses to Work" + by Forman and Danforth (Addison-Wesley 1999). */ + +static int +conservative_merge(PyObject *left, PyObject *right) +{ + int left_size; + int right_size; + int i, j, r, ok; + PyObject *temp, *rr; + + assert(PyList_Check(left)); + assert(PyList_Check(right)); + + again: + left_size = PyList_GET_SIZE(left); + right_size = PyList_GET_SIZE(right); + for (i = 0; i < left_size; i++) { + for (j = 0; j < right_size; j++) { + if (PyList_GET_ITEM(left, i) == + PyList_GET_ITEM(right, j)) { + /* found a merge point */ + temp = PyList_New(0); + if (temp == NULL) + return -1; + for (r = 0; r < j; r++) { + rr = PyList_GET_ITEM(right, r); + ok = PySequence_Contains(left, rr); + if (ok < 0) { + Py_DECREF(temp); + return -1; + } + if (!ok) { + ok = PyList_Append(temp, rr); + if (ok < 0) { + Py_DECREF(temp); + return -1; + } + } + } + ok = PyList_SetSlice(left, i, i, temp); + Py_DECREF(temp); + if (ok < 0) + return -1; + ok = PyList_SetSlice(right, 0, j+1, NULL); + if (ok < 0) + return -1; + goto again; + } + } + } + return PyList_SetSlice(left, left_size, left_size, right); +} + +static int +serious_order_disagreements(PyObject *left, PyObject *right) +{ + return 0; /* XXX later -- for now, we cheat: "don't do that" */ +} + +static PyObject * +mro_implementation(PyTypeObject *type) +{ + int i, n, ok; + PyObject *bases, *result; + + bases = type->tp_bases; + n = PyTuple_GET_SIZE(bases); + result = Py_BuildValue("[O]", (PyObject *)type); + if (result == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyTypeObject *base = + (PyTypeObject *) PyTuple_GET_ITEM(bases, i); + PyObject *parentMRO = PySequence_List(base->tp_mro); + if (parentMRO == NULL) { + Py_DECREF(result); + return NULL; + } + if (serious_order_disagreements(result, parentMRO)) { + Py_DECREF(result); + return NULL; + } + ok = conservative_merge(result, parentMRO); + Py_DECREF(parentMRO); + if (ok < 0) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + +static PyObject * +mro_external(PyObject *self, PyObject *args) +{ + PyTypeObject *type = (PyTypeObject *)self; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + return mro_implementation(type); +} + +static int +mro_internal(PyTypeObject *type) +{ + PyObject *mro, *result, *tuple; + + if (type->ob_type == &PyType_Type) { + result = mro_implementation(type); + } + else { + mro = PyObject_GetAttrString((PyObject *)type, "mro"); + if (mro == NULL) + return -1; + result = PyObject_CallObject(mro, NULL); + Py_DECREF(mro); + } + if (result == NULL) + return -1; + tuple = PySequence_Tuple(result); + Py_DECREF(result); + type->tp_mro = tuple; + return 0; +} + + +/* Calculate the best base amongst multiple base classes. + This is the first one that's on the path to the "solid base". */ + +static PyTypeObject * +best_base(PyObject *bases) +{ + int i, n; + PyTypeObject *base, *winner, *candidate, *base_i; + + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + assert(n > 0); + base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0); + winner = &PyBaseObject_Type; + for (i = 0; i < n; i++) { + base_i = (PyTypeObject *)PyTuple_GET_ITEM(bases, i); + if (!PyType_Check((PyObject *)base_i)) { + PyErr_SetString( + PyExc_TypeError, + "bases must be types"); + return NULL; + } + if (base_i->tp_dict == NULL) { + if (PyType_InitDict(base_i) < 0) + return NULL; + } + candidate = solid_base(base_i); + if (PyType_IsSubtype(winner, candidate)) + ; + else if (PyType_IsSubtype(candidate, winner)) { + winner = candidate; + base = base_i; + } + else { + PyErr_SetString( + PyExc_TypeError, + "multiple bases have " + "instance lay-out conflict"); + return NULL; + } + } + assert(base != NULL); + return base; +} + +static int +extra_ivars(PyTypeObject *type, PyTypeObject *base) +{ + int t_size = PyType_BASICSIZE(type); + int b_size = PyType_BASICSIZE(base); + + assert(t_size >= b_size); /* type smaller than base! */ + if (type->tp_itemsize || base->tp_itemsize) { + /* If itemsize is involved, stricter rules */ + return t_size != b_size || + type->tp_itemsize != base->tp_itemsize; + } + if (t_size == b_size) + return 0; + if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 && + type->tp_dictoffset == b_size && + (size_t)t_size == b_size + sizeof(PyObject *)) + return 0; /* "Forgive" adding a __dict__ only */ + return 1; +} + +static PyTypeObject * +solid_base(PyTypeObject *type) +{ + PyTypeObject *base; + + if (type->tp_base) + base = solid_base(type->tp_base); + else + base = &PyBaseObject_Type; + if (extra_ivars(type, base)) + return type; + else + return base; +} + +staticforward void object_dealloc(PyObject *); +staticforward int object_init(PyObject *, PyObject *, PyObject *); + +static PyObject * +type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +{ + PyObject *name, *bases, *dict; + static char *kwlist[] = {"name", "bases", "dict", 0}; + PyObject *slots, *tmp; + PyTypeObject *type, *base, *tmptype; + etype *et; + struct memberlist *mp; + int i, nbases, nslots, slotoffset, dynamic; + + if (metatype == &PyType_Type && + PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && + (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) { + /* type(x) -> x.__class__ */ + PyObject *x = PyTuple_GET_ITEM(args, 0); + Py_INCREF(x->ob_type); + return (PyObject *) x->ob_type; + } + + /* Check arguments */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO!O!:type", kwlist, + &name, + &PyTuple_Type, &bases, + &PyDict_Type, &dict)) + return NULL; + + /* Determine the proper metatype to deal with this, + and check for metatype conflicts while we're at it. + Note that if some other metatype wins to contract, + it's possible that its instances are not types. */ + nbases = PyTuple_GET_SIZE(bases); + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + tmptype = tmp->ob_type; + if (PyType_IsSubtype(metatype, tmptype)) + continue; + if (PyType_IsSubtype(tmptype, metatype)) { + metatype = tmptype; + continue; + } + PyErr_SetString(PyExc_TypeError, + "metatype conflict among bases"); + return NULL; + } + if (metatype->tp_new != type_new) /* Pass it to the winner */ + return metatype->tp_new(metatype, args, kwds); + + /* Adjust for empty tuple bases */ + if (nbases == 0) { + bases = Py_BuildValue("(O)", &PyBaseObject_Type); + if (bases == NULL) + return NULL; + nbases = 1; + } + else + Py_INCREF(bases); + + /* XXX From here until type is allocated, "return NULL" leaks bases! */ + + /* Calculate best base, and check that all bases are type objects */ + base = best_base(bases); + if (base == NULL) + return NULL; + if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { + PyErr_Format(PyExc_TypeError, + "type '%.100s' is not an acceptable base type", + base->tp_name); + return NULL; + } + + /* Should this be a dynamic class (i.e. modifiable __dict__)? */ + tmp = PyDict_GetItemString(dict, "__dynamic__"); + if (tmp != NULL) { + /* The class author has a preference */ + dynamic = PyObject_IsTrue(tmp); + Py_DECREF(tmp); + if (dynamic < 0) + return NULL; + } + else { + /* Make a new class dynamic if any of its bases is dynamic. + This is not always the same as inheriting the __dynamic__ + class attribute! */ + dynamic = 0; + for (i = 0; i < nbases; i++) { + tmptype = (PyTypeObject *)PyTuple_GET_ITEM(bases, i); + if (tmptype->tp_flags & Py_TPFLAGS_DYNAMICTYPE) { + dynamic = 1; + break; + } + } + } + + /* Check for a __slots__ sequence variable in dict, and count it */ + slots = PyDict_GetItemString(dict, "__slots__"); + nslots = 0; + if (slots != NULL) { + /* Make it into a tuple */ + if (PyString_Check(slots)) + slots = Py_BuildValue("(O)", slots); + else + slots = PySequence_Tuple(slots); + if (slots == NULL) + return NULL; + nslots = PyTuple_GET_SIZE(slots); + for (i = 0; i < nslots; i++) { + if (!PyString_Check(PyTuple_GET_ITEM(slots, i))) { + PyErr_SetString(PyExc_TypeError, + "__slots__ must be a sequence of strings"); + Py_DECREF(slots); + return NULL; + } + } + } + if (slots == NULL && base->tp_dictoffset == 0 && + (base->tp_setattro == PyObject_GenericSetAttr || + base->tp_setattro == NULL)) + nslots = 1; + + /* XXX From here until type is safely allocated, + "return NULL" may leak slots! */ + + /* Allocate the type object */ + type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); + if (type == NULL) + return NULL; + + /* Keep name and slots alive in the extended type object */ + et = (etype *)type; + Py_INCREF(name); + et->name = name; + et->slots = slots; + + /* Initialize essential fields */ + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | + Py_TPFLAGS_BASETYPE; + if (dynamic) + type->tp_flags |= Py_TPFLAGS_DYNAMICTYPE; + type->tp_as_number = &et->as_number; + type->tp_as_sequence = &et->as_sequence; + type->tp_as_mapping = &et->as_mapping; + type->tp_as_buffer = &et->as_buffer; + type->tp_name = PyString_AS_STRING(name); + + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + Py_INCREF(base); + type->tp_base = base; + + /* Initialize tp_defined from passed-in dict */ + type->tp_defined = dict = PyDict_Copy(dict); + if (dict == NULL) { + Py_DECREF(type); + return NULL; + } + + /* Special-case __new__: if it's a plain function, + make it a static function */ + tmp = PyDict_GetItemString(dict, "__new__"); + if (tmp != NULL && PyFunction_Check(tmp)) { + tmp = PyStaticMethod_New(tmp); + if (tmp == NULL) { + Py_DECREF(type); + return NULL; + } + PyDict_SetItemString(dict, "__new__", tmp); + Py_DECREF(tmp); + } + + /* Add descriptors for custom slots from __slots__, or for __dict__ */ + mp = et->members; + slotoffset = PyType_BASICSIZE(base); + if (slots != NULL) { + for (i = 0; i < nslots; i++, mp++) { + mp->name = PyString_AS_STRING( + PyTuple_GET_ITEM(slots, i)); + mp->type = T_OBJECT; + mp->offset = slotoffset; + slotoffset += sizeof(PyObject *); + } + } + else if (nslots) { + type->tp_dictoffset = slotoffset; + mp->name = "__dict__"; + mp->type = T_OBJECT; + mp->offset = slotoffset; + mp->readonly = 1; + slotoffset += sizeof(PyObject *); + } + type->tp_basicsize = slotoffset; + add_members(type, et->members); + + /* Special case some slots */ + if (type->tp_dictoffset != 0 || nslots > 0) { + if (base->tp_getattr == NULL && base->tp_getattro == NULL) + type->tp_getattro = PyObject_GenericGetAttr; + if (base->tp_setattr == NULL && base->tp_setattro == NULL) + type->tp_setattro = PyObject_GenericSetAttr; + } + type->tp_dealloc = subtype_dealloc; + + /* Always override allocation strategy to use regular heap */ + type->tp_alloc = PyType_GenericAlloc; + type->tp_free = _PyObject_Del; + + /* Initialize the rest */ + if (PyType_InitDict(type) < 0) { + Py_DECREF(type); + return NULL; + } + + /* Override slots that deserve it */ + override_slots(type, type->tp_defined); + return (PyObject *)type; +} + +/* Internal API to look for a name through the MRO. + This returns a borrowed reference, and doesn't set an exception! */ +PyObject * +_PyType_Lookup(PyTypeObject *type, PyObject *name) +{ + int i, n; + PyObject *mro, *res, *dict; + + /* For static types, look in tp_dict */ + if (!(type->tp_flags & Py_TPFLAGS_DYNAMICTYPE)) { + dict = type->tp_dict; + assert(dict && PyDict_Check(dict)); + return PyDict_GetItem(dict, name); + } + + /* For dynamic types, look in tp_defined of types in MRO */ + mro = type->tp_mro; + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + type = (PyTypeObject *) PyTuple_GET_ITEM(mro, i); + assert(PyType_Check(type)); + dict = type->tp_defined; + assert(dict && PyDict_Check(dict)); + res = PyDict_GetItem(dict, name); + if (res != NULL) + return res; + } + return NULL; +} + +/* This is similar to PyObject_GenericGetAttr(), + but uses _PyType_Lookup() instead of just looking in type->tp_dict. */ +static PyObject * +type_getattro(PyTypeObject *type, PyObject *name) +{ + PyTypeObject *metatype = type->ob_type; + PyObject *descr, *res; + descrgetfunc f; + + /* Initialize this type (we'll assume the metatype is initialized) */ + if (type->tp_dict == NULL) { + if (PyType_InitDict(type) < 0) + return NULL; + } + + /* Get a descriptor from the metatype */ + descr = _PyType_Lookup(metatype, name); + f = NULL; + if (descr != NULL) { + f = descr->ob_type->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) + return f(descr, + (PyObject *)type, (PyObject *)metatype); + } + + /* Look in tp_defined of this type and its bases */ + res = _PyType_Lookup(type, name); + if (res != NULL) { + f = res->ob_type->tp_descr_get; + if (f != NULL) + return f(res, (PyObject *)NULL, (PyObject *)type); + Py_INCREF(res); + return res; + } + + /* Use the descriptor from the metatype */ + if (f != NULL) { + res = f(descr, (PyObject *)type, (PyObject *)metatype); + return res; + } + if (descr != NULL) { + Py_INCREF(descr); + return descr; + } + + /* Give up */ + PyErr_Format(PyExc_AttributeError, + "type object '%.50s' has no attribute '%.400s'", + type->tp_name, PyString_AS_STRING(name)); + return NULL; +} + +static int +type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) +{ + if (type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) + return PyObject_GenericSetAttr((PyObject *)type, name, value); + PyErr_SetString(PyExc_TypeError, "can't set type attributes"); + return -1; +} + +static void +type_dealloc(PyTypeObject *type) +{ + etype *et; + + /* Assert this is a heap-allocated type object */ + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + et = (etype *)type; + Py_XDECREF(type->tp_base); + Py_XDECREF(type->tp_dict); + Py_XDECREF(type->tp_bases); + Py_XDECREF(type->tp_mro); + Py_XDECREF(type->tp_defined); + /* XXX more? */ + Py_XDECREF(et->name); + Py_XDECREF(et->slots); + type->ob_type->tp_free((PyObject *)type); +} + +static PyMethodDef type_methods[] = { + {"mro", mro_external, METH_VARARGS, + "mro() -> list\nreturn a type's method resolution order"}, + {0} +}; + +static char type_doc[] = +"type(object) -> the object's type\n" +"type(name, bases, dict) -> a new type"; + PyTypeObject PyType_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, /* Number of items for varobject */ - "type", /* Name of this type */ - sizeof(PyTypeObject), /* Basic object size */ - 0, /* Item size for varobject */ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)type_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - type_compare, /*tp_compare*/ - (reprfunc)type_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)_Py_HashPointer, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_xxx1*/ - 0, /*tp_xxx2*/ - 0, /*tp_xxx3*/ - 0, /*tp_xxx4*/ - "Define the behavior of a particular type of object.", + 0, /* ob_size */ + "type", /* tp_name */ + sizeof(etype), /* tp_basicsize */ + sizeof(struct memberlist), /* tp_itemsize */ + (destructor)type_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + type_compare, /* tp_compare */ + (reprfunc)type_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)_Py_HashPointer, /* tp_hash */ + (ternaryfunc)type_call, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)type_getattro, /* tp_getattro */ + (setattrofunc)type_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + type_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + type_methods, /* tp_methods */ + type_members, /* tp_members */ + type_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + type_new, /* tp_new */ }; + + +/* The base type of all types (eventually)... except itself. */ + +static int +object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static void +object_dealloc(PyObject *self) +{ + self->ob_type->tp_free(self); +} + +static void +object_free(PyObject *self) +{ + PyObject_Del(self); +} + +static struct memberlist object_members[] = { + {"__class__", T_OBJECT, offsetof(PyObject, ob_type), READONLY}, + {0} +}; + +PyTypeObject PyBaseObject_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "object", /* tp_name */ + sizeof(PyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)object_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "The most base type", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + object_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + object_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + object_free, /* tp_free */ +}; + + +/* Initialize the __dict__ in a type object */ + +static int +add_methods(PyTypeObject *type, PyMethodDef *meth) +{ + PyObject *dict = type->tp_defined; + + for (; meth->ml_name != NULL; meth++) { + PyObject *descr; + if (PyDict_GetItemString(dict, meth->ml_name)) + continue; + descr = PyDescr_NewMethod(type, meth); + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict,meth->ml_name,descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static int +add_wrappers(PyTypeObject *type, struct wrapperbase *base, void *wrapped) +{ + PyObject *dict = type->tp_defined; + + for (; base->name != NULL; base++) { + PyObject *descr; + if (PyDict_GetItemString(dict, base->name)) + continue; + descr = PyDescr_NewWrapper(type, base, wrapped); + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, base->name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static int +add_staticmethodwrappers(PyTypeObject *type, struct wrapperbase *base, + void *wrapped) +{ + PyObject *dict = type->tp_defined; + PyObject *sm; + + for (; base->name != NULL; base++) { + PyObject *descr; + if (PyDict_GetItemString(dict, base->name)) + continue; + descr = PyDescr_NewWrapper(type->ob_type, base, wrapped); + if (descr == NULL) + return -1; + sm = PyStaticMethod_New(descr); + Py_DECREF(descr); + if (sm == NULL) + return -1; + if (PyDict_SetItemString(dict, base->name, sm) < 0) + return -1; + Py_DECREF(sm); + } + return 0; +} + +static int +add_members(PyTypeObject *type, struct memberlist *memb) +{ + PyObject *dict = type->tp_defined; + + for (; memb->name != NULL; memb++) { + PyObject *descr; + if (PyDict_GetItemString(dict, memb->name)) + continue; + descr = PyDescr_NewMember(type, memb); + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, memb->name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static int +add_getset(PyTypeObject *type, struct getsetlist *gsp) +{ + PyObject *dict = type->tp_defined; + + for (; gsp->name != NULL; gsp++) { + PyObject *descr; + if (PyDict_GetItemString(dict, gsp->name)) + continue; + descr = PyDescr_NewGetSet(type, gsp); + + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, gsp->name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +staticforward int add_operators(PyTypeObject *); + +static int +inherit_slots(PyTypeObject *type, PyTypeObject *base) +{ + int oldsize, newsize; + +#undef COPYSLOT +#undef COPYNUM +#undef COPYSEQ +#undef COPYMAP +#define COPYSLOT(SLOT) \ + if (!type->SLOT) type->SLOT = base->SLOT + +#define COPYNUM(SLOT) COPYSLOT(tp_as_number->SLOT) +#define COPYSEQ(SLOT) COPYSLOT(tp_as_sequence->SLOT) +#define COPYMAP(SLOT) COPYSLOT(tp_as_mapping->SLOT) + + if (type->tp_as_number == NULL) + type->tp_as_number = base->tp_as_number; + else if (base->tp_as_number) { + COPYNUM(nb_add); + COPYNUM(nb_subtract); + COPYNUM(nb_multiply); + COPYNUM(nb_divide); + COPYNUM(nb_remainder); + COPYNUM(nb_divmod); + COPYNUM(nb_power); + COPYNUM(nb_negative); + COPYNUM(nb_positive); + COPYNUM(nb_absolute); + COPYNUM(nb_nonzero); + COPYNUM(nb_invert); + COPYNUM(nb_lshift); + COPYNUM(nb_rshift); + COPYNUM(nb_and); + COPYNUM(nb_xor); + COPYNUM(nb_or); + COPYNUM(nb_coerce); + COPYNUM(nb_int); + COPYNUM(nb_long); + COPYNUM(nb_float); + COPYNUM(nb_oct); + COPYNUM(nb_hex); + COPYNUM(nb_inplace_add); + COPYNUM(nb_inplace_subtract); + COPYNUM(nb_inplace_multiply); + COPYNUM(nb_inplace_divide); + COPYNUM(nb_inplace_remainder); + COPYNUM(nb_inplace_power); + COPYNUM(nb_inplace_lshift); + COPYNUM(nb_inplace_rshift); + COPYNUM(nb_inplace_and); + COPYNUM(nb_inplace_xor); + COPYNUM(nb_inplace_or); + } + + if (type->tp_as_sequence == NULL) + type->tp_as_sequence = base->tp_as_sequence; + else if (base->tp_as_sequence) { + COPYSEQ(sq_length); + COPYSEQ(sq_concat); + COPYSEQ(sq_repeat); + COPYSEQ(sq_item); + COPYSEQ(sq_slice); + COPYSEQ(sq_ass_item); + COPYSEQ(sq_ass_slice); + COPYSEQ(sq_contains); + COPYSEQ(sq_inplace_concat); + COPYSEQ(sq_inplace_repeat); + } + + if (type->tp_as_mapping == NULL) + type->tp_as_mapping = base->tp_as_mapping; + else if (base->tp_as_mapping) { + COPYMAP(mp_length); + COPYMAP(mp_subscript); + COPYMAP(mp_ass_subscript); + } + + /* Special flag magic */ + if (!type->tp_as_buffer && base->tp_as_buffer) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_GETCHARBUFFER; + type->tp_flags |= + base->tp_flags & Py_TPFLAGS_HAVE_GETCHARBUFFER; + } + if (!type->tp_as_sequence && base->tp_as_sequence) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_SEQUENCE_IN; + type->tp_flags |= base->tp_flags & Py_TPFLAGS_HAVE_SEQUENCE_IN; + } + if ((type->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS) != + (base->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS)) { + if ((!type->tp_as_number && base->tp_as_number) || + (!type->tp_as_sequence && base->tp_as_sequence)) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_INPLACEOPS; + if (!type->tp_as_number && !type->tp_as_sequence) { + type->tp_flags |= base->tp_flags & + Py_TPFLAGS_HAVE_INPLACEOPS; + } + } + /* Wow */ + } + if (!type->tp_as_number && base->tp_as_number) { + type->tp_flags &= ~Py_TPFLAGS_CHECKTYPES; + type->tp_flags |= base->tp_flags & Py_TPFLAGS_CHECKTYPES; + } + + /* Copying basicsize is connected to the GC flags */ + oldsize = PyType_BASICSIZE(base); + newsize = type->tp_basicsize ? PyType_BASICSIZE(type) : oldsize; + if (!(type->tp_flags & Py_TPFLAGS_GC) && + (base->tp_flags & Py_TPFLAGS_GC) && + (type->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE/*GC slots exist*/) && + (!type->tp_traverse && !type->tp_clear)) { + type->tp_flags |= Py_TPFLAGS_GC; + COPYSLOT(tp_traverse); + COPYSLOT(tp_clear); + } + PyType_SET_BASICSIZE(type, newsize); + + COPYSLOT(tp_itemsize); + COPYSLOT(tp_dealloc); + COPYSLOT(tp_print); + if (type->tp_getattr == NULL && type->tp_getattro == NULL) { + type->tp_getattr = base->tp_getattr; + type->tp_getattro = base->tp_getattro; + } + if (type->tp_setattr == NULL && type->tp_setattro == NULL) { + type->tp_setattr = base->tp_setattr; + type->tp_setattro = base->tp_setattro; + } + /* tp_compare see tp_richcompare */ + COPYSLOT(tp_repr); + COPYSLOT(tp_hash); + COPYSLOT(tp_call); + COPYSLOT(tp_str); + COPYSLOT(tp_as_buffer); + COPYSLOT(tp_flags); + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE) { + if (type->tp_compare == NULL && type->tp_richcompare == NULL) { + type->tp_compare = base->tp_compare; + type->tp_richcompare = base->tp_richcompare; + } + } + else { + COPYSLOT(tp_compare); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_WEAKREFS) { + COPYSLOT(tp_weaklistoffset); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_ITER) { + COPYSLOT(tp_iter); + COPYSLOT(tp_iternext); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) { + COPYSLOT(tp_descr_get); + COPYSLOT(tp_descr_set); + COPYSLOT(tp_dictoffset); + COPYSLOT(tp_init); + COPYSLOT(tp_alloc); + COPYSLOT(tp_new); + COPYSLOT(tp_free); + } + + return 0; +} + +int +PyType_InitDict(PyTypeObject *type) +{ + PyObject *dict, *bases, *x; + PyTypeObject *base; + int i, n; + + if (type->tp_dict != NULL) + return 0; /* Already initialized */ + + /* Initialize tp_base (defaults to BaseObject unless that's us) */ + base = type->tp_base; + if (base == NULL && type != &PyBaseObject_Type) + base = type->tp_base = &PyBaseObject_Type; + + /* Initialize tp_bases */ + bases = type->tp_bases; + if (bases == NULL) { + if (base == NULL) + bases = PyTuple_New(0); + else + bases = Py_BuildValue("(O)", base); + if (bases == NULL) + return -1; + type->tp_bases = bases; + } + + /* Initialize the base class */ + if (base) { + if (PyType_InitDict(base) < 0) + return -1; + } + + /* Initialize tp_defined */ + dict = type->tp_defined; + if (dict == NULL) { + dict = PyDict_New(); + if (dict == NULL) + return -1; + type->tp_defined = dict; + } + + /* Add type-specific descriptors to tp_defined */ + if (add_operators(type) < 0) + return -1; + if (type->tp_methods != NULL) { + if (add_methods(type, type->tp_methods) < 0) + return -1; + } + if (type->tp_members != NULL) { + if (add_members(type, type->tp_members) < 0) + return -1; + } + if (type->tp_getset != NULL) { + if (add_getset(type, type->tp_getset) < 0) + return -1; + } + + /* Temporarily make tp_dict the same object as tp_defined. + (This is needed to call mro(), and can stay this way for + dynamic types). */ + Py_INCREF(type->tp_defined); + type->tp_dict = type->tp_defined; + + /* Calculate method resolution order */ + if (mro_internal(type) < 0) { + return -1; + } + + /* Initialize tp_dict properly */ + if (!PyType_HasFeature(type, Py_TPFLAGS_DYNAMICTYPE)) { + /* For a static type, tp_dict is the consolidation + of the tp_defined of its bases in MRO. Earlier + bases override later bases; since d.update() works + the other way, we walk the MRO sequence backwards. */ + Py_DECREF(type->tp_dict); + type->tp_dict = PyDict_New(); + if (type->tp_dict == NULL) + return -1; + bases = type->tp_mro; + assert(bases != NULL); + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + for (i = n; --i >= 0; ) { + base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i); + assert(PyType_Check(base)); + x = base->tp_defined; + if (x != NULL && PyDict_Update(type->tp_dict, x) < 0) + return -1; + } + } + + /* Inherit slots from direct base */ + if (type->tp_base != NULL) + if (inherit_slots(type, type->tp_base) < 0) + return -1; + + return 0; +} + + +/* Generic wrappers for overloadable 'operators' such as __getitem__ */ + +/* There's a wrapper *function* for each distinct function typedef used + for type object slots (e.g. binaryfunc, ternaryfunc, etc.). There's a + wrapper *table* for each distinct operation (e.g. __len__, __add__). + Most tables have only one entry; the tables for binary operators have two + entries, one regular and one with reversed arguments. */ + +static PyObject * +wrap_inquiry(PyObject *self, PyObject *args, void *wrapped) +{ + inquiry func = (inquiry)wrapped; + int res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong((long)res); +} + +static struct wrapperbase tab_len[] = { + {"__len__", (wrapperfunc)wrap_inquiry, "x.__len__() <==> len(x)"}, + {0} +}; + +static PyObject * +wrap_binaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + binaryfunc func = (binaryfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + return (*func)(self, other); +} + +static PyObject * +wrap_binaryfunc_r(PyObject *self, PyObject *args, void *wrapped) +{ + binaryfunc func = (binaryfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + return (*func)(other, self); +} + +#undef BINARY +#define BINARY(NAME, OP) \ +static struct wrapperbase tab_##NAME[] = { \ + {"__" #NAME "__", \ + (wrapperfunc)wrap_binaryfunc, \ + "x.__" #NAME "__(y) <==> " #OP}, \ + {"__r" #NAME "__", \ + (wrapperfunc)wrap_binaryfunc_r, \ + "y.__r" #NAME "__(x) <==> " #OP}, \ + {0} \ +} + +BINARY(add, "x+y"); +BINARY(sub, "x-y"); +BINARY(mul, "x*y"); +BINARY(div, "x/y"); +BINARY(mod, "x%y"); +BINARY(divmod, "divmod(x,y)"); +BINARY(lshift, "x<>y"); +BINARY(and, "x&y"); +BINARY(xor, "x^y"); +BINARY(or, "x|y"); + +static PyObject * +wrap_ternaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + ternaryfunc func = (ternaryfunc)wrapped; + PyObject *other; + PyObject *third = Py_None; + + /* Note: This wrapper only works for __pow__() */ + + if (!PyArg_ParseTuple(args, "O|O", &other, &third)) + return NULL; + return (*func)(self, other, third); +} + +#undef TERNARY +#define TERNARY(NAME, OP) \ +static struct wrapperbase tab_##NAME[] = { \ + {"__" #NAME "__", \ + (wrapperfunc)wrap_ternaryfunc, \ + "x.__" #NAME "__(y, z) <==> " #OP}, \ + {"__r" #NAME "__", \ + (wrapperfunc)wrap_ternaryfunc, \ + "y.__r" #NAME "__(x, z) <==> " #OP}, \ + {0} \ +} + +TERNARY(pow, "(x**y) % z"); + +#undef UNARY +#define UNARY(NAME, OP) \ +static struct wrapperbase tab_##NAME[] = { \ + {"__" #NAME "__", \ + (wrapperfunc)wrap_unaryfunc, \ + "x.__" #NAME "__() <==> " #OP}, \ + {0} \ +} + +static PyObject * +wrap_unaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + unaryfunc func = (unaryfunc)wrapped; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + return (*func)(self); +} + +UNARY(neg, "-x"); +UNARY(pos, "+x"); +UNARY(abs, "abs(x)"); +UNARY(nonzero, "x != 0"); +UNARY(invert, "~x"); +UNARY(int, "int(x)"); +UNARY(long, "long(x)"); +UNARY(float, "float(x)"); +UNARY(oct, "oct(x)"); +UNARY(hex, "hex(x)"); + +#undef IBINARY +#define IBINARY(NAME, OP) \ +static struct wrapperbase tab_##NAME[] = { \ + {"__" #NAME "__", \ + (wrapperfunc)wrap_binaryfunc, \ + "x.__" #NAME "__(y) <==> " #OP}, \ + {0} \ +} + +IBINARY(iadd, "x+=y"); +IBINARY(isub, "x-=y"); +IBINARY(imul, "x*=y"); +IBINARY(idiv, "x/=y"); +IBINARY(imod, "x%=y"); +IBINARY(ilshift, "x<<=y"); +IBINARY(irshift, "x>>=y"); +IBINARY(iand, "x&=y"); +IBINARY(ixor, "x^=y"); +IBINARY(ior, "x|=y"); + +#undef ITERNARY +#define ITERNARY(NAME, OP) \ +static struct wrapperbase tab_##NAME[] = { \ + {"__" #NAME "__", \ + (wrapperfunc)wrap_ternaryfunc, \ + "x.__" #NAME "__(y) <==> " #OP}, \ + {0} \ +} + +ITERNARY(ipow, "x = (x**y) % z"); + +static struct wrapperbase tab_getitem[] = { + {"__getitem__", (wrapperfunc)wrap_binaryfunc, + "x.__getitem__(y) <==> x[y]"}, + {0} +}; + +static PyObject * +wrap_intargfunc(PyObject *self, PyObject *args, void *wrapped) +{ + intargfunc func = (intargfunc)wrapped; + int i; + + if (!PyArg_ParseTuple(args, "i", &i)) + return NULL; + return (*func)(self, i); +} + +static struct wrapperbase tab_mul_int[] = { + {"__mul__", (wrapperfunc)wrap_intargfunc, "x.__mul__(n) <==> x*n"}, + {"__rmul__", (wrapperfunc)wrap_intargfunc, "x.__rmul__(n) <==> n*x"}, + {0} +}; + +static struct wrapperbase tab_concat[] = { + {"__add__", (wrapperfunc)wrap_binaryfunc, "x.__add__(y) <==> x+y"}, + {0} +}; + +static struct wrapperbase tab_imul_int[] = { + {"__imul__", (wrapperfunc)wrap_intargfunc, "x.__imul__(n) <==> x*=n"}, + {0} +}; + +static struct wrapperbase tab_getitem_int[] = { + {"__getitem__", (wrapperfunc)wrap_intargfunc, + "x.__getitem__(i) <==> x[i]"}, + {0} +}; + +static PyObject * +wrap_intintargfunc(PyObject *self, PyObject *args, void *wrapped) +{ + intintargfunc func = (intintargfunc)wrapped; + int i, j; + + if (!PyArg_ParseTuple(args, "ii", &i, &j)) + return NULL; + return (*func)(self, i, j); +} + +static struct wrapperbase tab_getslice[] = { + {"__getslice__", (wrapperfunc)wrap_intintargfunc, + "x.__getslice__(i, j) <==> x[i:j]"}, + {0} +}; + +static PyObject * +wrap_intobjargproc(PyObject *self, PyObject *args, void *wrapped) +{ + intobjargproc func = (intobjargproc)wrapped; + int i, res; + PyObject *value; + + if (!PyArg_ParseTuple(args, "iO", &i, &value)) + return NULL; + res = (*func)(self, i, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_setitem_int[] = { + {"__setitem__", (wrapperfunc)wrap_intobjargproc, + "x.__setitem__(i, y) <==> x[i]=y"}, + {0} +}; + +static PyObject * +wrap_intintobjargproc(PyObject *self, PyObject *args, void *wrapped) +{ + intintobjargproc func = (intintobjargproc)wrapped; + int i, j, res; + PyObject *value; + + if (!PyArg_ParseTuple(args, "iiO", &i, &j, &value)) + return NULL; + res = (*func)(self, i, j, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_setslice[] = { + {"__setslice__", (wrapperfunc)wrap_intintobjargproc, + "x.__setslice__(i, j, y) <==> x[i:j]=y"}, + {0} +}; + +/* XXX objobjproc is a misnomer; should be objargpred */ +static PyObject * +wrap_objobjproc(PyObject *self, PyObject *args, void *wrapped) +{ + objobjproc func = (objobjproc)wrapped; + int res; + PyObject *value; + + if (!PyArg_ParseTuple(args, "O", &value)) + return NULL; + res = (*func)(self, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong((long)res); +} + +static struct wrapperbase tab_contains[] = { + {"__contains__", (wrapperfunc)wrap_objobjproc, + "x.__contains__(y) <==> y in x"}, + {0} +}; + +static PyObject * +wrap_objobjargproc(PyObject *self, PyObject *args, void *wrapped) +{ + objobjargproc func = (objobjargproc)wrapped; + int res; + PyObject *key, *value; + + if (!PyArg_ParseTuple(args, "OO", &key, &value)) + return NULL; + res = (*func)(self, key, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_setitem[] = { + {"__setitem__", (wrapperfunc)wrap_objobjargproc, + "x.__setitem__(y, z) <==> x[y]=z"}, + {0} +}; + +static PyObject * +wrap_cmpfunc(PyObject *self, PyObject *args, void *wrapped) +{ + cmpfunc func = (cmpfunc)wrapped; + int res; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + res = (*func)(self, other); + if (PyErr_Occurred()) + return NULL; + return PyInt_FromLong((long)res); +} + +static struct wrapperbase tab_cmp[] = { + {"__cmp__", (wrapperfunc)wrap_cmpfunc, + "x.__cmp__(y) <==> cmp(x,y)"}, + {0} +}; + +static struct wrapperbase tab_repr[] = { + {"__repr__", (wrapperfunc)wrap_unaryfunc, + "x.__repr__() <==> repr(x)"}, + {0} +}; + +static struct wrapperbase tab_getattr[] = { + {"__getattr__", (wrapperfunc)wrap_binaryfunc, + "x.__getattr__('name') <==> x.name"}, + {0} +}; + +static PyObject * +wrap_setattr(PyObject *self, PyObject *args, void *wrapped) +{ + setattrofunc func = (setattrofunc)wrapped; + int res; + PyObject *name, *value; + + if (!PyArg_ParseTuple(args, "OO", &name, &value)) + return NULL; + res = (*func)(self, name, value); + if (res < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_delattr(PyObject *self, PyObject *args, void *wrapped) +{ + setattrofunc func = (setattrofunc)wrapped; + int res; + PyObject *name; + + if (!PyArg_ParseTuple(args, "O", &name)) + return NULL; + res = (*func)(self, name, NULL); + if (res < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_setattr[] = { + {"__setattr__", (wrapperfunc)wrap_setattr, + "x.__setattr__('name', value) <==> x.name = value"}, + {"__delattr__", (wrapperfunc)wrap_delattr, + "x.__delattr__('name') <==> del x.name"}, + {0} +}; + +static PyObject * +wrap_hashfunc(PyObject *self, PyObject *args, void *wrapped) +{ + hashfunc func = (hashfunc)wrapped; + long res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong(res); +} + +static struct wrapperbase tab_hash[] = { + {"__hash__", (wrapperfunc)wrap_hashfunc, + "x.__hash__() <==> hash(x)"}, + {0} +}; + +static PyObject * +wrap_call(PyObject *self, PyObject *args, void *wrapped) +{ + ternaryfunc func = (ternaryfunc)wrapped; + + /* XXX What about keyword arguments? */ + return (*func)(self, args, NULL); +} + +static struct wrapperbase tab_call[] = { + {"__call__", (wrapperfunc)wrap_call, + "x.__call__(...) <==> x(...)"}, + {0} +}; + +static struct wrapperbase tab_str[] = { + {"__str__", (wrapperfunc)wrap_unaryfunc, + "x.__str__() <==> str(x)"}, + {0} +}; + +static PyObject * +wrap_richcmpfunc(PyObject *self, PyObject *args, void *wrapped, int op) +{ + richcmpfunc func = (richcmpfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + return (*func)(self, other, op); +} + +#undef RICHCMP_WRAPPER +#define RICHCMP_WRAPPER(NAME, OP) \ +static PyObject * \ +richcmp_##NAME(PyObject *self, PyObject *args, void *wrapped) \ +{ \ + return wrap_richcmpfunc(self, args, wrapped, OP); \ +} + +RICHCMP_WRAPPER(lt, Py_LT); +RICHCMP_WRAPPER(le, Py_LE); +RICHCMP_WRAPPER(eq, Py_EQ); +RICHCMP_WRAPPER(ne, Py_NE); +RICHCMP_WRAPPER(gt, Py_GT); +RICHCMP_WRAPPER(ge, Py_GE); + +#undef RICHCMP_ENTRY +#define RICHCMP_ENTRY(NAME, EXPR) \ + {"__" #NAME "__", (wrapperfunc)richcmp_##NAME, \ + "x.__" #NAME "__(y) <==> " EXPR} + +static struct wrapperbase tab_richcmp[] = { + RICHCMP_ENTRY(lt, "xy"), + RICHCMP_ENTRY(ge, "x>=y"), + {0} +}; + +static struct wrapperbase tab_iter[] = { + {"__iter__", (wrapperfunc)wrap_unaryfunc, "x.__iter__() <==> iter(x)"}, + {0} +}; + +static PyObject * +wrap_next(PyObject *self, PyObject *args, void *wrapped) +{ + unaryfunc func = (unaryfunc)wrapped; + PyObject *res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == NULL && !PyErr_Occurred()) + PyErr_SetNone(PyExc_StopIteration); + return res; +} + +static struct wrapperbase tab_next[] = { + {"next", (wrapperfunc)wrap_next, + "x.next() -> the next value, or raise StopIteration"}, + {0} +}; + +static PyObject * +wrap_descr_get(PyObject *self, PyObject *args, void *wrapped) +{ + descrgetfunc func = (descrgetfunc)wrapped; + PyObject *obj; + PyObject *type = NULL; + + if (!PyArg_ParseTuple(args, "O|O", &obj, &type)) + return NULL; + if (type == NULL) + type = (PyObject *)obj->ob_type; + return (*func)(self, obj, type); +} + +static struct wrapperbase tab_descr_get[] = { + {"__get__", (wrapperfunc)wrap_descr_get, + "descr.__get__(obj, type) -> value"}, + {0} +}; + +static PyObject * +wrap_descrsetfunc(PyObject *self, PyObject *args, void *wrapped) +{ + descrsetfunc func = (descrsetfunc)wrapped; + PyObject *obj, *value; + int ret; + + if (!PyArg_ParseTuple(args, "OO", &obj, &value)) + return NULL; + ret = (*func)(self, obj, value); + if (ret < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_descr_set[] = { + {"__set__", (wrapperfunc)wrap_descrsetfunc, + "descr.__set__(obj, value)"}, + {0} +}; + +static PyObject * +wrap_init(PyObject *self, PyObject *args, void *wrapped) +{ + initproc func = (initproc)wrapped; + + /* XXX What about keyword arguments? */ + if (func(self, args, NULL) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct wrapperbase tab_init[] = { + {"__init__", (wrapperfunc)wrap_init, + "x.__init__(...) initializes x; " + "see x.__type__.__doc__ for signature"}, + {0} +}; + +static PyObject * +wrap_new(PyObject *type, PyObject *args, void *wrapped) +{ + newfunc new = (newfunc)wrapped; + return new((PyTypeObject *)type, args, NULL); +} + +static struct wrapperbase tab_new[] = { + {"__new__", (wrapperfunc)wrap_new, + "T.__new__() -> an object with type T"}, + {0} +}; + +static int +add_operators(PyTypeObject *type) +{ + PySequenceMethods *sq; + PyMappingMethods *mp; + PyNumberMethods *nb; + +#undef ADD +#define ADD(SLOT, TABLE) \ + if (SLOT) { \ + if (add_wrappers(type, TABLE, (void *)(SLOT)) < 0) \ + return -1; \ + } + + if ((sq = type->tp_as_sequence) != NULL) { + ADD(sq->sq_length, tab_len); + ADD(sq->sq_concat, tab_concat); + ADD(sq->sq_repeat, tab_mul_int); + ADD(sq->sq_item, tab_getitem_int); + ADD(sq->sq_slice, tab_getslice); + ADD(sq->sq_ass_item, tab_setitem_int); + ADD(sq->sq_ass_slice, tab_setslice); + ADD(sq->sq_contains, tab_contains); + ADD(sq->sq_inplace_concat, tab_iadd); + ADD(sq->sq_inplace_repeat, tab_imul_int); + } + + if ((mp = type->tp_as_mapping) != NULL) { + if (sq->sq_length == NULL) + ADD(mp->mp_length, tab_len); + ADD(mp->mp_subscript, tab_getitem); + ADD(mp->mp_ass_subscript, tab_setitem); + } + + /* We don't support "old-style numbers" because their binary + operators require that both arguments have the same type; + the wrappers here only work for new-style numbers. */ + if ((type->tp_flags & Py_TPFLAGS_CHECKTYPES) && + (nb = type->tp_as_number) != NULL) { + ADD(nb->nb_add, tab_add); + ADD(nb->nb_subtract, tab_sub); + ADD(nb->nb_multiply, tab_mul); + ADD(nb->nb_divide, tab_div); + ADD(nb->nb_remainder, tab_mod); + ADD(nb->nb_divmod, tab_divmod); + ADD(nb->nb_power, tab_pow); + ADD(nb->nb_negative, tab_neg); + ADD(nb->nb_positive, tab_pos); + ADD(nb->nb_absolute, tab_abs); + ADD(nb->nb_nonzero, tab_nonzero); + ADD(nb->nb_invert, tab_invert); + ADD(nb->nb_lshift, tab_lshift); + ADD(nb->nb_rshift, tab_rshift); + ADD(nb->nb_and, tab_and); + ADD(nb->nb_xor, tab_xor); + ADD(nb->nb_or, tab_or); + /* We don't support coerce() -- see above comment */ + ADD(nb->nb_int, tab_int); + ADD(nb->nb_long, tab_long); + ADD(nb->nb_float, tab_float); + ADD(nb->nb_oct, tab_oct); + ADD(nb->nb_hex, tab_hex); + ADD(nb->nb_inplace_add, tab_iadd); + ADD(nb->nb_inplace_subtract, tab_isub); + ADD(nb->nb_inplace_multiply, tab_imul); + ADD(nb->nb_inplace_divide, tab_idiv); + ADD(nb->nb_inplace_remainder, tab_imod); + ADD(nb->nb_inplace_power, tab_ipow); + ADD(nb->nb_inplace_lshift, tab_ilshift); + ADD(nb->nb_inplace_rshift, tab_irshift); + ADD(nb->nb_inplace_and, tab_iand); + ADD(nb->nb_inplace_xor, tab_ixor); + ADD(nb->nb_inplace_or, tab_ior); + } + + ADD(type->tp_getattro, tab_getattr); + ADD(type->tp_setattro, tab_setattr); + ADD(type->tp_compare, tab_cmp); + ADD(type->tp_repr, tab_repr); + ADD(type->tp_hash, tab_hash); + ADD(type->tp_call, tab_call); + ADD(type->tp_str, tab_str); + ADD(type->tp_richcompare, tab_richcmp); + ADD(type->tp_iter, tab_iter); + ADD(type->tp_iternext, tab_next); + ADD(type->tp_descr_get, tab_descr_get); + ADD(type->tp_descr_set, tab_descr_set); + ADD(type->tp_init, tab_init); + + if (type->tp_new != NULL) + add_staticmethodwrappers(type, tab_new, + (void *)(type->tp_new)); + + return 0; +} + +/* Slot wrappers that call the corresponding __foo__ slot */ + +#define SLOT0(SLOTNAME, OPNAME) \ +static PyObject * \ +slot_##SLOTNAME(PyObject *self) \ +{ \ + return PyObject_CallMethod(self, "__" #OPNAME "__", ""); \ +} + +#define SLOT1(SLOTNAME, OPNAME, ARG1TYPE, ARGCODES) \ +static PyObject * \ +slot_##SLOTNAME(PyObject *self, ARG1TYPE arg1) \ +{ \ + return PyObject_CallMethod(self, "__" #OPNAME "__", #ARGCODES, arg1); \ +} + +#define SLOT2(SLOTNAME, OPNAME, ARG1TYPE, ARG2TYPE, ARGCODES) \ +static PyObject * \ +slot_##SLOTNAME(PyObject *self, ARG1TYPE arg1, ARG2TYPE arg2) \ +{ \ + return PyObject_CallMethod(self, "__" #OPNAME "__", \ + #ARGCODES, arg1, arg2); \ +} + +static int +slot_sq_length(PyObject *self) +{ + PyObject *res = PyObject_CallMethod(self, "__len__", ""); + + if (res == NULL) + return -1; + return (int)PyInt_AsLong(res); +} + +SLOT1(sq_concat, add, PyObject *, O); +SLOT1(sq_repeat, mul, int, i); +SLOT1(sq_item, getitem, int, i); +SLOT2(sq_slice, getslice, int, int, ii); + +static int +slot_sq_ass_item(PyObject *self, int index, PyObject *value) +{ + PyObject *res; + + if (value == NULL) + res = PyObject_CallMethod(self, "__delitem__", "i", index); + else + res = PyObject_CallMethod(self, "__setitem__", + "iO", index, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_sq_ass_slice(PyObject *self, int i, int j, PyObject *value) +{ + PyObject *res; + + if (value == NULL) + res = PyObject_CallMethod(self, "__delslice__", "ii", i, j); + else + res = PyObject_CallMethod(self, "__setslice__", + "iiO", i, j, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_sq_contains(PyObject *self, PyObject *value) +{ + PyObject *res = PyObject_CallMethod(self, "__contains__", "O", value); + int r; + + if (res == NULL) + return -1; + r = PyInt_AsLong(res); + Py_DECREF(res); + return r; +} + +SLOT1(sq_inplace_concat, iadd, PyObject *, O); +SLOT1(sq_inplace_repeat, imul, int, i); + +#define slot_mp_length slot_sq_length + +SLOT1(mp_subscript, getitem, PyObject *, O); + +static int +slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value) +{ + PyObject *res; + + if (value == NULL) + res = PyObject_CallMethod(self, "__delitem__", "O", key); + else + res = PyObject_CallMethod(self, "__setitem__", + "OO", key, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +/* XXX the numerical slots should call the reverse operators too; + but how do they know their type? */ +SLOT1(nb_add, add, PyObject *, O); +SLOT1(nb_subtract, sub, PyObject *, O); +SLOT1(nb_multiply, mul, PyObject *, O); +SLOT1(nb_divide, div, PyObject *, O); +SLOT1(nb_remainder, mod, PyObject *, O); +SLOT1(nb_divmod, divmod, PyObject *, O); +SLOT2(nb_power, pow, PyObject *, PyObject *, OO); +SLOT0(nb_negative, neg); +SLOT0(nb_positive, pos); +SLOT0(nb_absolute, abs); + +static int +slot_nb_nonzero(PyObject *self) +{ + PyObject *res = PyObject_CallMethod(self, "__nonzero__", ""); + + if (res == NULL) + return -1; + return (int)PyInt_AsLong(res); +} + +SLOT0(nb_invert, invert); +SLOT1(nb_lshift, lshift, PyObject *, O); +SLOT1(nb_rshift, rshift, PyObject *, O); +SLOT1(nb_and, and, PyObject *, O); +SLOT1(nb_xor, xor, PyObject *, O); +SLOT1(nb_or, or, PyObject *, O); +/* Not coerce() */ +SLOT0(nb_int, int); +SLOT0(nb_long, long); +SLOT0(nb_float, float); +SLOT0(nb_oct, oct); +SLOT0(nb_hex, hex); +SLOT1(nb_inplace_add, iadd, PyObject *, O); +SLOT1(nb_inplace_subtract, isub, PyObject *, O); +SLOT1(nb_inplace_multiply, imul, PyObject *, O); +SLOT1(nb_inplace_divide, idiv, PyObject *, O); +SLOT1(nb_inplace_remainder, imod, PyObject *, O); +SLOT2(nb_inplace_power, ipow, PyObject *, PyObject *, OO); +SLOT1(nb_inplace_lshift, ilshift, PyObject *, O); +SLOT1(nb_inplace_rshift, irshift, PyObject *, O); +SLOT1(nb_inplace_and, iand, PyObject *, O); +SLOT1(nb_inplace_xor, ixor, PyObject *, O); +SLOT1(nb_inplace_or, ior, PyObject *, O); + +static int +slot_tp_compare(PyObject *self, PyObject *other) +{ + PyObject *res = PyObject_CallMethod(self, "__cmp__", "O", other); + long r; + + if (res == NULL) + return -1; + r = PyInt_AsLong(res); + Py_DECREF(res); + return (int)r; +} + +SLOT0(tp_repr, repr); + +static long +slot_tp_hash(PyObject *self) +{ + PyObject *res = PyObject_CallMethod(self, "__hash__", ""); + long h; + + if (res == NULL) + return -1; + h = PyInt_AsLong(res); + if (h == -1 && !PyErr_Occurred()) + h = -2; + return h; +} + +static PyObject * +slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *meth = PyObject_GetAttrString(self, "__call__"); + PyObject *res; + + if (meth == NULL) + return NULL; + res = PyObject_Call(meth, args, kwds); + Py_DECREF(meth); + return res; +} + +SLOT0(tp_str, str); + +static PyObject * +slot_tp_getattro(PyObject *self, PyObject *name) +{ + PyTypeObject *tp = self->ob_type; + PyObject *dict = NULL; + PyObject *getattr; + + if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) + dict = tp->tp_dict; + if (dict == NULL) { + PyErr_Format(PyExc_SystemError, + "'%.100s' type object has no __dict__???", + tp->tp_name); + return NULL; + } + getattr = PyDict_GetItemString(dict, "__getattr__"); + if (getattr == NULL) { + PyErr_SetString(PyExc_AttributeError, "__getattr__"); + return NULL; + } + return PyObject_CallFunction(getattr, "OO", self, name); +} + +static int +slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + PyObject *res; + + if (value == NULL) + res = PyObject_CallMethod(self, "__delattr__", "O", name); + else + res = PyObject_CallMethod(self, "__setattr__", + "OO", name, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +/* Map rich comparison operators to their __xx__ namesakes */ +static char *name_op[] = { + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", +}; + +static PyObject * +slot_tp_richcompare(PyObject *self, PyObject *other, int op) +{ + PyObject *meth = PyObject_GetAttrString(self, name_op[op]); + PyObject *res; + + if (meth == NULL) + return NULL; + res = PyObject_CallFunction(meth, "O", other); + Py_DECREF(meth); + return res; +} + +SLOT0(tp_iter, iter); + +static PyObject * +slot_tp_iternext(PyObject *self) +{ + return PyObject_CallMethod(self, "next", ""); +} + +SLOT2(tp_descr_get, get, PyObject *, PyObject *, OO); + +static int +slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value) +{ + PyObject *res = PyObject_CallMethod(self, "__set__", + "OO", target, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *meth = PyObject_GetAttrString(self, "__init__"); + PyObject *res; + + if (meth == NULL) + return -1; + res = PyObject_Call(meth, args, kwds); + Py_DECREF(meth); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static PyObject * +slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func = PyObject_GetAttrString((PyObject *)type, "__new__"); + PyObject *newargs, *x; + int i, n; + + if (func == NULL) + return NULL; + assert(PyTuple_Check(args)); + n = PyTuple_GET_SIZE(args); + newargs = PyTuple_New(n+1); + if (newargs == NULL) + return NULL; + Py_INCREF(type); + PyTuple_SET_ITEM(newargs, 0, (PyObject *)type); + for (i = 0; i < n; i++) { + x = PyTuple_GET_ITEM(args, i); + Py_INCREF(x); + PyTuple_SET_ITEM(newargs, i+1, x); + } + x = PyObject_Call(func, newargs, kwds); + Py_DECREF(func); + return x; +} + +static void +override_slots(PyTypeObject *type, PyObject *dict) +{ + PySequenceMethods *sq = type->tp_as_sequence; + PyMappingMethods *mp = type->tp_as_mapping; + PyNumberMethods *nb = type->tp_as_number; + +#define SQSLOT(OPNAME, SLOTNAME) \ + if (PyDict_GetItemString(dict, OPNAME)) { \ + sq->SLOTNAME = slot_##SLOTNAME; \ + } + +#define MPSLOT(OPNAME, SLOTNAME) \ + if (PyDict_GetItemString(dict, OPNAME)) { \ + mp->SLOTNAME = slot_##SLOTNAME; \ + } + +#define NBSLOT(OPNAME, SLOTNAME) \ + if (PyDict_GetItemString(dict, OPNAME)) { \ + nb->SLOTNAME = slot_##SLOTNAME; \ + } + +#define TPSLOT(OPNAME, SLOTNAME) \ + if (PyDict_GetItemString(dict, OPNAME)) { \ + type->SLOTNAME = slot_##SLOTNAME; \ + } + + SQSLOT("__len__", sq_length); + SQSLOT("__add__", sq_concat); + SQSLOT("__mul__", sq_repeat); + SQSLOT("__getitem__", sq_item); + SQSLOT("__getslice__", sq_slice); + SQSLOT("__setitem__", sq_ass_item); + SQSLOT("__delitem__", sq_ass_item); + SQSLOT("__setslice__", sq_ass_slice); + SQSLOT("__delslice__", sq_ass_slice); + SQSLOT("__contains__", sq_contains); + SQSLOT("__iadd__", sq_inplace_concat); + SQSLOT("__imul__", sq_inplace_repeat); + + MPSLOT("__len__", mp_length); + MPSLOT("__getitem__", mp_subscript); + MPSLOT("__setitem__", mp_ass_subscript); + MPSLOT("__delitem__", mp_ass_subscript); + + NBSLOT("__add__", nb_add); + NBSLOT("__sub__", nb_subtract); + NBSLOT("__mul__", nb_multiply); + NBSLOT("__div__", nb_divide); + NBSLOT("__mod__", nb_remainder); + NBSLOT("__divmod__", nb_divmod); + NBSLOT("__pow__", nb_power); + NBSLOT("__neg__", nb_negative); + NBSLOT("__pos__", nb_positive); + NBSLOT("__abs__", nb_absolute); + NBSLOT("__nonzero__", nb_nonzero); + NBSLOT("__invert__", nb_invert); + NBSLOT("__lshift__", nb_lshift); + NBSLOT("__rshift__", nb_rshift); + NBSLOT("__and__", nb_and); + NBSLOT("__xor__", nb_xor); + NBSLOT("__or__", nb_or); + /* Not coerce() */ + NBSLOT("__int__", nb_int); + NBSLOT("__long__", nb_long); + NBSLOT("__float__", nb_float); + NBSLOT("__oct__", nb_oct); + NBSLOT("__hex__", nb_hex); + NBSLOT("__iadd__", nb_inplace_add); + NBSLOT("__isub__", nb_inplace_subtract); + NBSLOT("__imul__", nb_inplace_multiply); + NBSLOT("__idiv__", nb_inplace_divide); + NBSLOT("__imod__", nb_inplace_remainder); + NBSLOT("__ipow__", nb_inplace_power); + NBSLOT("__ilshift__", nb_inplace_lshift); + NBSLOT("__irshift__", nb_inplace_rshift); + NBSLOT("__iand__", nb_inplace_and); + NBSLOT("__ixor__", nb_inplace_xor); + NBSLOT("__ior__", nb_inplace_or); + + if (PyDict_GetItemString(dict, "__str__") || + PyDict_GetItemString(dict, "__repr__")) + type->tp_print = NULL; + + TPSLOT("__cmp__", tp_compare); + TPSLOT("__repr__", tp_repr); + TPSLOT("__hash__", tp_hash); + TPSLOT("__call__", tp_call); + TPSLOT("__str__", tp_str); + TPSLOT("__getattr__", tp_getattro); + TPSLOT("__setattr__", tp_setattro); + TPSLOT("__lt__", tp_richcompare); + TPSLOT("__le__", tp_richcompare); + TPSLOT("__eq__", tp_richcompare); + TPSLOT("__ne__", tp_richcompare); + TPSLOT("__gt__", tp_richcompare); + TPSLOT("__ge__", tp_richcompare); + TPSLOT("__iter__", tp_iter); + TPSLOT("next", tp_iternext); + TPSLOT("__get__", tp_descr_get); + TPSLOT("__set__", tp_descr_set); + TPSLOT("__init__", tp_init); + TPSLOT("__new__", tp_new); +} diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 4bdff5a314e..1319c7c52ad 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4667,12 +4667,6 @@ static PyMethodDef unicode_methods[] = { {NULL, NULL} }; -static PyObject * -unicode_getattr(PyUnicodeObject *self, char *name) -{ - return Py_FindMethod(unicode_methods, (PyObject*) self, name); -} - static PySequenceMethods unicode_as_sequence = { (inquiry) unicode_length, /* sq_length */ (binaryfunc) PyUnicode_Concat, /* sq_concat */ @@ -5346,6 +5340,30 @@ static PyBufferProcs unicode_as_buffer = { (getcharbufferproc) unicode_buffer_getcharbuf, }; +static PyObject * +unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + static char *kwlist[] = {"string", "encoding", "errors", 0}; + char *encoding = NULL; + char *errors = NULL; + + assert(type == &PyUnicode_Type); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:unicode", + kwlist, &x, &encoding, &errors)) + return NULL; + if (x == NULL) + return (PyObject *)_PyUnicode_New(0); + return PyUnicode_FromEncodedObject(x, encoding, errors); +} + +static char unicode_doc[] = +"unicode(string [, encoding[, errors]]) -> object\n\ +\n\ +Create a new Unicode object from the given encoded string.\n\ +encoding defaults to the current default string encoding and \n\ +errors, defining the error handling, to 'strict'."; + PyTypeObject PyUnicode_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ @@ -5355,7 +5373,7 @@ PyTypeObject PyUnicode_Type = { /* Slots */ (destructor)_PyUnicode_Free, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)unicode_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc) unicode_compare, /* tp_compare */ (reprfunc) unicode_repr, /* tp_repr */ @@ -5365,10 +5383,28 @@ PyTypeObject PyUnicode_Type = { (hashfunc) unicode_hash, /* tp_hash*/ 0, /* tp_call*/ (reprfunc) unicode_str, /* tp_str */ - (getattrofunc) NULL, /* tp_getattro */ - (setattrofunc) NULL, /* tp_setattro */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ &unicode_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ + unicode_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + unicode_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + unicode_new, /* tp_new */ }; /* Initialize the Unicode implementation */ diff --git a/PC/config.c b/PC/config.c index 2044688009b..df9b89abbe2 100644 --- a/PC/config.c +++ b/PC/config.c @@ -44,6 +44,7 @@ extern void init_locale(void); extern void init_codecs(void); extern void initxreadlines(void); extern void init_weakref(void); +extern void initxxsubtype(void); /* XXX tim: what's the purpose of ADDMODULE MARKER? */ /* -- ADDMODULE MARKER 1 -- */ @@ -98,6 +99,8 @@ struct _inittab _PyImport_Inittab[] = { {"xreadlines", initxreadlines}, {"_weakref", init_weakref}, + {"xxsubtype", initxxsubtype}, + /* XXX tim: what's the purpose of ADDMODULE MARKER? */ /* -- ADDMODULE MARKER 2 -- */ diff --git a/PCbuild/pythoncore.dsp b/PCbuild/pythoncore.dsp index 83e80162796..4995caed4ee 100644 --- a/PCbuild/pythoncore.dsp +++ b/PCbuild/pythoncore.dsp @@ -495,6 +495,21 @@ SOURCE=..\Modules\cStringIO.c # End Source File # Begin Source File +SOURCE=..\Objects\descrobject.c + +!IF "$(CFG)" == "pythoncore - Win32 Release" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Debug" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Alpha Debug" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Alpha Release" + +!ENDIF + +# End Source File +# Begin Source File + SOURCE=..\Objects\dictobject.c !IF "$(CFG)" == "pythoncore - Win32 Release" @@ -1737,6 +1752,21 @@ SOURCE=..\Modules\xreadlinesmodule.c # End Source File # Begin Source File +SOURCE=..\Modules\xxsubtype.c + +!IF "$(CFG)" == "pythoncore - Win32 Release" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Debug" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Alpha Debug" + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Alpha Release" + +!ENDIF + +# End Source File +# Begin Source File + SOURCE=..\Modules\yuvconvert.c !IF "$(CFG)" == "pythoncore - Win32 Release" diff --git a/PLAN.txt b/PLAN.txt new file mode 100644 index 00000000000..1ab384ccea0 --- /dev/null +++ b/PLAN.txt @@ -0,0 +1,431 @@ +Project: core implementation +**************************** + +Tasks: + +Do binary operators properly. nb_add should try to call self.__add__ +and other.__radd__. I think I'll exclude base types that define any +binary operator without setting the CHECKTYPES flag. + +Fix comparisons. There's some nasty stuff here: when two types are +not the same, and they're not instances, the fallback code doesn't +account for the possibility that they might be subtypes of a common +base type that defines a comparison. + +Fix subtype_dealloc(). This currently searches through the list of +base types until it finds a type whose tp_dealloc is not +subtype_dealloc. I think this is not safe. I think the alloc/dealloc +policy needs to be rethought. *** There's an idea here that I haven't +worked out yet: just as object creation now has separate API's tp_new, +tp_alloc, and tp_init, destruction has tp_dealloc and tp_free. (Maybe +tp_fini should be added to correspond to tp_init?) Something +could/should be done with this. *** + +Clean up isinstance(), issubclass() and their C equivalents. There +are a bunch of different APIs here and not all of them do the right +thing yet. There should be fewer APIs and their implementation should +be simpler. The old "abstract subclass" test should probably +disappear (if we want to root out ExtensionClass). *** I think I've +done 90% of this by creating PyType_IsSubtype() and using it +appropriately. For now, the old "abstract subclass" test is still +there, and there may be some places where PyObject_IsSubclass() is +called where PyType_IsSubtype() would be more appropriate. *** + +Check for conflicts between base classes. I fear that the rules used +to decide whether multiple bases have conflicting instance variables +aren't strict enough. I think that sometimes two different classes +adding __dict__ may be incompatible after all. + +Check for order conflicts. Suppose there are two base classes X and +Y. Suppose class B derives from X and Y, and class C from Y and X (in +that order). Now suppose class D derives from B and C. In which +order should the base classes X and Y be searched? This is an order +conflict, and should be disallowed; currently the test for this is not +implemented. + +Clean up the GC interface. Currently, tp_basicsize includes the GC +head size iff tp_flags includes the GC flag bit. This makes object +size math a pain (e.g. to see if two object types have the same +instance size, you can't just compare the tp_basicsize fields -- you +have to conditionally subtract the GC head size). Neil has a patch +that improves the API in this area, but it's backwards incompatible. +(http://sf.net/tracker/?func=detail&aid=421893&group_id=5470&atid=305470) +I think I know of a way to fix the incompatibility (by switching to a +different flag bit). *** Tim proposed a better idea: macros to access +tp_basicsize while hiding the nastiness. This is done now, so I think +the rest of this task needn't be done. *** + +Make the __dict__ of types declared with Python class statements +writable -- only statically declared types must have an immutable +dict, because they're shared between interpreter instances. Possibly +trap writes to the __dict__ to update the corresponding tp_ if +an ____ name is affected. *** Done as part of the next task. *** + +It should be an option (maybe a different metaclass, maybe a flag) to +*not* merge __dict__ with all the bases, but instead search the +__dict__ (or __introduced__?) of all bases in __mro__ order. (This is +needed anyway to unify classes completely.) *** Partly done. +Inheritance of slots from bases is still icky: (1) MRO is not always +respected when inheriting slots; (2) dynamic classes can't add slot +implementations in Python after creation (e.g., setting C.__hash__ +doesn't set the tp_hash slot). *** + +Universal base class (object). How can we make the object class +subclassable and define simple default methods for everything without +having these inherited by built-in types that don't want these +defaults? *** Done, really. *** + +Add error checking to the MRO calculation. *** Done. *** + +Make __new__ overridable through a Python class method (!). Make more +of the sub-algorithms of type construction available as methods. *** +After I implemented class methods, I found that in order to be able +to make an upcall to Base.__new__() and have it create an instance of +your class (rather than a Base instance), you can't use class methods +-- you must use static methods. So I've implemented those too. I've +hooked up __new__ in the right places, so the first part of this is +now done. I've also exported the MRO calculation and made it +overridable, as metamethod mro(). I believe that closes this topic +for now. I expect that some warts will only be really debugged when +we try to use this for some, eh, interesting types such as tuples. *** + +More -- I'm sure new issues will crop up as we go. + + +Project: loose ends and follow-through +************************************** + +Tasks: + +Make more (most?) built-in types act as their own factory functions. + +Make more (most?) built-in types subtypable -- with or without +overridable allocation. *** This includes descriptors! It should be +possible to write descriptors in Python, so metaclasses can do clever +things with them. *** + +Exceptions should be types. This changes the rules, since now almost +anything can be raised (as maybe it should). Or should we strive for +enforcement of the convention that all exceptions should be derived +from Exception? String exceptions will be another hassle, to be +deprecated and eventually ruled out. + +Standardize a module containing names for all built-in types, and +standardize on names. E.g. should the official name of the string +type be 'str', 'string', or 'StringType'? + +Create a hierarchy of types, so that e.g. int and long are both +subtypes of an abstract base type integer, which is itself a subtype +of number, etc. A lot of thinking can go into this! + +*** NEW TASK??? *** +Implement "signature" objects. These are alluded to in PEP 252 but +not yet specified. Supposedly they provide an easily usable API to +find out about function/method arguments. Building these for Python +functions is simple. Building these for built-in functions will +require a change to the PyMethodDef structure, so that a type can +provide signature information for its C methods. (This would also +help in supporting keyword arguments for C methods with less work than +PyArg_ParseTupleAndKeywords() currently requires.) But should we do +this? It's additional work and not required for any of the other +parts. + + +Project: making classes use the new machinery +********************************************* + +Tasks: + +Try to get rid of all code in classobject.c by deferring to the new +mechanisms. How far can we get without breaking backwards +compatibility? This is underspecified because I haven't thought much +about it yet. Can we lose the use of PyInstance_Check() everywhere? +I would hope so! + + +Project: backwards compatibility +******************************** + +Tasks: + +Make sure all code checks the proper tp_flags bit before accessing +type object fields. + +Identify areas of incompatibility with Python 2.1. Design solutions. +Implement and test. + +Some specific areas: a fair amount of code probably depends on +specific types having __members__ and/or __methods__ attributes. +These are currently not present (conformant to PEP 252, which proposes +to drop them) but we may have to add them back. This can be done in a +generic way with not too much effort. Tim adds: Perhaps that dir(object) +rarely returns anything but [] now is a consequence of this. I'm very +used to doing, e.g., dir([]) or dir("") in an interactive shell to jog my +memory; also one of the reasons test_generators failed. + +Another area: going all the way with classes and instances means that +type(x) == types.InstanceType won't work any more to detect instances. +Should there be a mode where this still works? Maybe this should be +the default mode, with a warning, and an explicit way to get the new +way to work? (Instead of a __future__ statement, I'm thinking of a +module global __metaclass__ which would provide the default metaclass +for baseless class statements.) + + +Project: testing +**************** + +Tasks: + +Identify new functionality that needs testing. Conceive unit tests +for all new functionality. Conceive stress tests for critical +features. Run the tests. Fix bugs. Repeat until satisfied. + +Note: this may interact with the branch integration task. + + +Project: integration with main branch +************************************* + +Tasks: + +Merge changes in the HEAD branch into the descr-branch. Then merge +the descr-branch back into the HEAD branch. + +The longer we wait, the more effort this will be -- the descr-branch +forked off quite a long time ago, and there are changes everywhere in +the HEAD branch (e.g. the dict object has been radically rewritten). + +On the other hand, if we do this too early, we'll have to do it again +later. + +Note from Tim: We should never again wait until literally 100s of files +are out of synch. I don't care how often I need to do this, provided only +that it's a tractable task each time. Once per week sounds like a good +idea. As is, even the trunk change to rangeobject.c created more than its +proper share of merge headaches, because it confused all the other reasons +include file merges were getting conflicts (the more changes there are, the +worse diff does; indeed, I came up with the ndiff algorithm in the 80s +precisely because the source-control diff program Cray used at the time +produced minimal but *senseless* diffs, thus creating artificial conflicts; +paying unbounded attention to context does a much better job of putting +changes where they make semantic sense too; but we're stuck with Unix diff +here, and it isn't robust in this sense; if we don't keep its job simple, +it will make my job hell). + +Done: +To undo or rename before final merge: Modules/spam.c has worked its +way into the branch Unix and Windows builds (pythoncore.dsp and +PC/config.c); also imported by test_descr.py. How about renaming to +xxsubtype.c (whatever) now? + + +Project: performance tuning +*************************** + +Tasks: + +Pick or create a general performance benchmark for Python. Benchmark +the new system vs. the old system. Profile the new system. Improve +hotspots. Repeat until satisfied. + +Note: this may interact with the branch integration task. + + +Project: documentation +********************** + +Tasks: + +Update PEP 252 (descriptors). Describe more of the prototype +implementation + +Update PEP 253 (subtyping). Complicated architectural wrangling with +metaclasses. There is an interaction between implementation and +description. + +Write PEP 254 (unification of classes). This should discuss what +changes for ordinary classes, and how we can make it more b/w +compatible. + +Other documentation. There needs to be user documentation, +eventually. + + +Project: community interaction +****************************** + +Tasks: + +Once the PEPs are written, solicit community feedback, and formulate +responses to the feedback. Give the community enough time to think +over this complicated proposal. Provide the community with a +prototype implementation to test. Try to do this *before* casting +everything in stone! + +MERGE BEGIN **************************************************************** +Merge details (this section is Tim's scratchpad, but should help a lot if +he dies of frustration while wrestling with CVS <0.9 wink>). +---------------------------------------------------------------------------- +2001-08-01 Merging descr-branch back into trunk. + +Tagged trunk about 22:05: + cvs tag date2001-08-01 python + +Merged trunk delta into branch: + cvs -q -z3 up -j date2001-07-30 -j date2001-08-01 descr + +No conflicts (! first time ever!) ... but problems with pythoncore.dsp. +Resolved. + +Rebuilt from scratch; ran all tests; checked into branch about 22:40. + +Merged descr-branch back into trunk: + cvs -q -z3 up -j descr-branch python + +34 conflicts. Hmm! OK, looks like every file in the project with an +embedded RCS Id is "a conflict". Others make no sense, e.g., a dozen +conflicts in dictobject.c, sometimes enclosing identical(!) blobs of +source code. And CVS remains utterly baffled by Python type object decls. +Every line of ceval.c's generator code si in conflict blocks ... OK, +there's no pattern or sense here, I'll just deal with it. + +Conflicts resolved; rebuilt from scratch; test_weakref fails. +---------------------------------------------------------------------------- +2001-07-30 + +Doing this again while the expat and Windows installer changes are still +fresh on my mind. + +Tagged trunk about 23:50 EDT on the 29th: + cvs tag date2001-07-30 python + +Merged trunk delta into branch: + + cvs -q -z3 up -j date2001-07-28 -j date2001-07-30 descr + +2 conflicts, resolved. +---------------------------------------------------------------------------- +2001-07-28 + +Tagged trunk about 00:31 EDT: + cvs tag date2001-07-28 python + +Merged trunk delta into branch: + cvs -q -z3 up -j date2001-07-21 -j date2001-07-28 descr + +4 conflicts, all RCS Ids. Resolved. +---------------------------------------------------------------------------- +2001-07-21 + +Tagged trunk about 01:00 EDT: + cvs tag date2001-07-21 python + +Merged trunk delta into branch: + cvs -q -z3 up -j date2001-07-17b -j date2001-07-21 descr + +4 conflicts, mostly RCS Id thingies. Resolved. + +Legit failure in new test_repr, because repr.py dispatches on the exact +string returned by type(x). type(1L) and type('s') differ in descr-branch +now, and repr.py didn't realize that, falling back to the "unknown type" +case for longs and strings. Repaired descr-branch repr.py. +---------------------------------------------------------------------------- +2001-07-19 + +Removed the r22a1-branch tag (see next entry). Turns out Guido did add a +r22a1 tag, so the r22a1-branch tag served no point anymore. +---------------------------------------------------------------------------- +2001-07-18 2.2a1 releaase + +Immediately after the merge just below, I tagged descr-branch via + + cvs tag r22a1-branch descr + +Guido may or may not want to add another tag here (? maybe he wants to do +some more Unix fiddling first). +---------------------------------------------------------------------------- +2001-07-17 building 2.2a1 release, from descr-branch + +Tagged trunk about 22:00 EDT, like so: + cvs tag date2001-07-17b python + +Merged trunk delta into branch via: + cvs -q -z3 up -j date2001-07-17a -j date2001-07-17b descr +---------------------------------------------------------------------------- +2001-07-17 + +Tagged trunk about 00:05 EDT, like so: + cvs tag date2001-07-17a python + +Merged trunk delta into branch via: + cvs -q -z3 up -j date2001-07-16 -j date2001-07-17a descr +---------------------------------------------------------------------------- +2001-07-16 + +Tagged trunk about 15:20 EDT, like so: + cvs tag date2001-07-16 python + +Guido then added all the other dist/ directories to descr-branch from that +trunk tag. + +Tim then merged trunk delta into the branch via: + cvs -q -z3 up -j date2001-07-15 -j date2001-07-16 descr +---------------------------------------------------------------------------- +2001-07-15 + +Tagged trunk about 15:44 EDT, like so: + cvs tag date2001-07-15 python + +Merged trunk delta into branch via: + cvs -q -z3 up -j date2001-07-13 -j date2001-07-15 descr + +Four files with conflicts, all artificial RCS Id & Revision thingies. +Resolved and committed. +---------------------------------------------------------------------------- +2001-07-13 + +Tagged trunk about 22:13 EDT, like so: + cvs tag date2001-07-13 python + +Merged trunk delta into branch via: + cvs -q -z3 up -j date2001-07-06 -j date2001-07-13 descr + +Six(!) files with conflicts, mostly related to NeilS's generator gc patches. +Unsure why, but CVS seems always to think there are conflicts whenever a +line in a type object decl gets changed, and the conflict marking seems +maximally confused in these cases. Anyway, since I reviewed those patches +on the trunk, good thing I'm merging them, and darned glad it's still fresh +on my mind. + +Resolved the conflicts, and committed the changes in a few hours total. +---------------------------------------------------------------------------- +2001-07-07 + +Merge of trunk tag date2001-07-06 into descr-branch, via + cvs -q -z3 up -j date2001-07-06 mergedescr +was committed on 2001-07-07. + +Merge issues: + +(all resolved -- GvR) +---------------------------------------------------------------------------- +2001-07-06 + +Tagged trunk a bit after midnight, like so: + +C:\Code>cvs tag date2001-07-06 python +cvs server: Tagging python +cvs server: Tagging python/dist +cvs server: Tagging python/dist/src +T python/dist/src/.cvsignore +T python/dist/src/LICENSE +T python/dist/src/Makefile.pre.in +T python/dist/src/README +... [& about 3000 lines more] ... + +This is the first trunk snapshot to be merged into the descr-branch. +Gave it a date instead of a goofy name because there's going to be more +than one of these, and at least it's obvious which of two ISO dates comes +earlier. These tags should go away after all merging is complete. +MERGE END ****************************************************************** diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index ec55928cccc..ec3c5fc0a04 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -131,26 +131,6 @@ start of the object (or at the specified offset). The slice will\n\ extend to the end of the target object (or with the specified size)."; -static PyObject * -builtin_unicode(PyObject *self, PyObject *args) -{ - PyObject *v; - char *encoding = NULL; - char *errors = NULL; - - if ( !PyArg_ParseTuple(args, "O|ss:unicode", &v, &encoding, &errors) ) - return NULL; - return PyUnicode_FromEncodedObject(v, encoding, errors); -} - -static char unicode_doc[] = -"unicode(string [, encoding[, errors]]) -> object\n\ -\n\ -Create a new Unicode object from the given encoded string.\n\ -encoding defaults to the current default string encoding and \n\ -errors, defining the error handling, to 'strict'."; - - static PyObject * builtin_callable(PyObject *self, PyObject *args) { @@ -435,257 +415,6 @@ The mode must be 'exec' to compile a module, 'single' to compile a\n\ single (interactive) statement, or 'eval' to compile an expression."; -#ifndef WITHOUT_COMPLEX - -static PyObject * -complex_from_string(PyObject *v) -{ - extern double strtod(const char *, char **); - const char *s, *start; - char *end; - double x=0.0, y=0.0, z; - int got_re=0, got_im=0, done=0; - int digit_or_dot; - int sw_error=0; - int sign; - char buffer[256]; /* For errors */ - char s_buffer[256]; - int len; - - if (PyString_Check(v)) { - s = PyString_AS_STRING(v); - len = PyString_GET_SIZE(v); - } - else if (PyUnicode_Check(v)) { - if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { - PyErr_SetString(PyExc_ValueError, - "complex() literal too large to convert"); - return NULL; - } - if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v), - PyUnicode_GET_SIZE(v), - s_buffer, - NULL)) - return NULL; - s = s_buffer; - len = (int)strlen(s); - } - else if (PyObject_AsCharBuffer(v, &s, &len)) { - PyErr_SetString(PyExc_TypeError, - "complex() arg is not a string"); - return NULL; - } - - /* position on first nonblank */ - start = s; - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (s[0] == '\0') { - PyErr_SetString(PyExc_ValueError, - "complex() arg is an empty string"); - return NULL; - } - - z = -1.0; - sign = 1; - do { - - switch (*s) { - - case '\0': - if (s-start != len) { - PyErr_SetString( - PyExc_ValueError, - "complex() arg contains a null byte"); - return NULL; - } - if(!done) sw_error=1; - break; - - case '-': - sign = -1; - /* Fallthrough */ - case '+': - if (done) sw_error=1; - s++; - if ( *s=='\0'||*s=='+'||*s=='-' || - isspace(Py_CHARMASK(*s)) ) sw_error=1; - break; - - case 'J': - case 'j': - if (got_im || done) { - sw_error = 1; - break; - } - if (z<0.0) { - y=sign; - } - else{ - y=sign*z; - } - got_im=1; - s++; - if (*s!='+' && *s!='-' ) - done=1; - break; - - default: - if (isspace(Py_CHARMASK(*s))) { - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (s[0] != '\0') - sw_error=1; - else - done = 1; - break; - } - digit_or_dot = - (*s=='.' || isdigit(Py_CHARMASK(*s))); - if (done||!digit_or_dot) { - sw_error=1; - break; - } - errno = 0; - PyFPE_START_PROTECT("strtod", return 0) - z = strtod(s, &end) ; - PyFPE_END_PROTECT(z) - if (errno != 0) { - sprintf(buffer, - "float() out of range: %.150s", s); - PyErr_SetString( - PyExc_ValueError, - buffer); - return NULL; - } - s=end; - if (*s=='J' || *s=='j') { - - break; - } - if (got_re) { - sw_error=1; - break; - } - - /* accept a real part */ - x=sign*z; - got_re=1; - if (got_im) done=1; - z = -1.0; - sign = 1; - break; - - } /* end of switch */ - - } while (*s!='\0' && !sw_error); - - if (sw_error) { - PyErr_SetString(PyExc_ValueError, - "complex() arg is a malformed string"); - return NULL; - } - - return PyComplex_FromDoubles(x,y); -} - -static PyObject * -builtin_complex(PyObject *self, PyObject *args) -{ - PyObject *r, *i, *tmp; - PyNumberMethods *nbr, *nbi = NULL; - Py_complex cr, ci; - int own_r = 0; - - i = NULL; - if (!PyArg_ParseTuple(args, "O|O:complex", &r, &i)) - return NULL; - if (PyString_Check(r) || PyUnicode_Check(r)) - return complex_from_string(r); - if ((nbr = r->ob_type->tp_as_number) == NULL || - nbr->nb_float == NULL || - (i != NULL && - ((nbi = i->ob_type->tp_as_number) == NULL || - nbi->nb_float == NULL))) { - PyErr_SetString(PyExc_TypeError, - "complex() arg can't be converted to complex"); - return NULL; - } - /* XXX Hack to support classes with __complex__ method */ - if (PyInstance_Check(r)) { - static PyObject *complexstr; - PyObject *f; - if (complexstr == NULL) { - complexstr = PyString_InternFromString("__complex__"); - if (complexstr == NULL) - return NULL; - } - f = PyObject_GetAttr(r, complexstr); - if (f == NULL) - PyErr_Clear(); - else { - PyObject *args = Py_BuildValue("()"); - if (args == NULL) - return NULL; - r = PyEval_CallObject(f, args); - Py_DECREF(args); - Py_DECREF(f); - if (r == NULL) - return NULL; - own_r = 1; - } - } - if (PyComplex_Check(r)) { - cr = ((PyComplexObject*)r)->cval; - if (own_r) { - Py_DECREF(r); - } - } - else { - tmp = PyNumber_Float(r); - if (own_r) { - Py_DECREF(r); - } - if (tmp == NULL) - return NULL; - if (!PyFloat_Check(tmp)) { - PyErr_SetString(PyExc_TypeError, - "float(r) didn't return a float"); - Py_DECREF(tmp); - return NULL; - } - cr.real = PyFloat_AsDouble(tmp); - Py_DECREF(tmp); - cr.imag = 0.0; - } - if (i == NULL) { - ci.real = 0.0; - ci.imag = 0.0; - } - else if (PyComplex_Check(i)) - ci = ((PyComplexObject*)i)->cval; - else { - tmp = (*nbi->nb_float)(i); - if (tmp == NULL) - return NULL; - ci.real = PyFloat_AsDouble(tmp); - Py_DECREF(tmp); - ci.imag = 0.; - } - cr.real -= ci.imag; - cr.imag += ci.real; - return PyComplex_FromCComplex(cr); -} - -static char complex_doc[] = -"complex(real[, imag]) -> complex number\n\ -\n\ -Create a complex number from a real part and an optional imaginary part.\n\ -This is equivalent to (real + imag*1j) where imag defaults to 0."; - - -#endif - static PyObject * builtin_dir(PyObject *self, PyObject *args) { @@ -1060,8 +789,8 @@ builtin_map(PyObject *self, PyObject *args) } if (curlen < 0) curlen = 8; /* arbitrary */ - if (curlen > len) - len = curlen; + if (curlen > len) + len = curlen; } /* Get space for the result list. */ @@ -1300,91 +1029,6 @@ Return the string itself or the previously interned string object with the\n\ same value."; -static PyObject * -builtin_int(PyObject *self, PyObject *args) -{ - PyObject *v; - int base = -909; /* unlikely! */ - - if (!PyArg_ParseTuple(args, "O|i:int", &v, &base)) - return NULL; - if (base == -909) - return PyNumber_Int(v); - else if (PyString_Check(v)) - return PyInt_FromString(PyString_AS_STRING(v), NULL, base); - else if (PyUnicode_Check(v)) - return PyInt_FromUnicode(PyUnicode_AS_UNICODE(v), - PyUnicode_GET_SIZE(v), - base); - else { - PyErr_SetString(PyExc_TypeError, - "int() can't convert non-string with explicit base"); - return NULL; - } -} - -static char int_doc[] = -"int(x[, base]) -> integer\n\ -\n\ -Convert a string or number to an integer, if possible. A floating point\n\ -argument will be truncated towards zero (this does not include a string\n\ -representation of a floating point number!) When converting a string, use\n\ -the optional base. It is an error to supply a base when converting a\n\ -non-string."; - - -static PyObject * -builtin_long(PyObject *self, PyObject *args) -{ - PyObject *v; - int base = -909; /* unlikely! */ - - if (!PyArg_ParseTuple(args, "O|i:long", &v, &base)) - return NULL; - if (base == -909) - return PyNumber_Long(v); - else if (PyString_Check(v)) - return PyLong_FromString(PyString_AS_STRING(v), NULL, base); - else if (PyUnicode_Check(v)) - return PyLong_FromUnicode(PyUnicode_AS_UNICODE(v), - PyUnicode_GET_SIZE(v), - base); - else { - PyErr_SetString(PyExc_TypeError, - "long() can't convert non-string with explicit base"); - return NULL; - } -} - -static char long_doc[] = -"long(x) -> long integer\n\ -long(x, base) -> long integer\n\ -\n\ -Convert a string or number to a long integer, if possible. A floating\n\ -point argument will be truncated towards zero (this does not include a\n\ -string representation of a floating point number!) When converting a\n\ -string, use the given base. It is an error to supply a base when\n\ -converting a non-string."; - - -static PyObject * -builtin_float(PyObject *self, PyObject *args) -{ - PyObject *v; - - if (!PyArg_ParseTuple(args, "O:float", &v)) - return NULL; - if (PyString_Check(v)) - return PyFloat_FromString(v, NULL); - return PyNumber_Float(v); -} - -static char float_doc[] = -"float(x) -> floating point number\n\ -\n\ -Convert a string or number to a floating point number, if possible."; - - static PyObject * builtin_iter(PyObject *self, PyObject *args) { @@ -1431,22 +1075,6 @@ static char len_doc[] = Return the number of items of a sequence or mapping."; -static PyObject * -builtin_list(PyObject *self, PyObject *args) -{ - PyObject *v; - - if (!PyArg_ParseTuple(args, "O:list", &v)) - return NULL; - return PySequence_List(v); -} - -static char list_doc[] = -"list(sequence) -> list\n\ -\n\ -Return a new list whose items are the same as those of the argument sequence."; - - static PyObject * builtin_slice(PyObject *self, PyObject *args) { @@ -2032,58 +1660,6 @@ Round a number to a given precision in decimal digits (default 0 digits).\n\ This always returns a floating point number. Precision may be negative."; -static PyObject * -builtin_str(PyObject *self, PyObject *args) -{ - PyObject *v; - - if (!PyArg_ParseTuple(args, "O:str", &v)) - return NULL; - return PyObject_Str(v); -} - -static char str_doc[] = -"str(object) -> string\n\ -\n\ -Return a nice string representation of the object.\n\ -If the argument is a string, the return value is the same object."; - - -static PyObject * -builtin_tuple(PyObject *self, PyObject *args) -{ - PyObject *v; - - if (!PyArg_ParseTuple(args, "O:tuple", &v)) - return NULL; - return PySequence_Tuple(v); -} - -static char tuple_doc[] = -"tuple(sequence) -> list\n\ -\n\ -Return a tuple whose items are the same as those of the argument sequence.\n\ -If the argument is a tuple, the return value is the same object."; - - -static PyObject * -builtin_type(PyObject *self, PyObject *args) -{ - PyObject *v; - - if (!PyArg_ParseTuple(args, "O:type", &v)) - return NULL; - v = (PyObject *)v->ob_type; - Py_INCREF(v); - return v; -} - -static char type_doc[] = -"type(object) -> type object\n\ -\n\ -Return the type of the object."; - - static PyObject * builtin_vars(PyObject *self, PyObject *args) { @@ -2255,16 +1831,12 @@ static PyMethodDef builtin_methods[] = { {"cmp", builtin_cmp, 1, cmp_doc}, {"coerce", builtin_coerce, 1, coerce_doc}, {"compile", builtin_compile, 1, compile_doc}, -#ifndef WITHOUT_COMPLEX - {"complex", builtin_complex, 1, complex_doc}, -#endif {"delattr", builtin_delattr, 1, delattr_doc}, {"dir", builtin_dir, 1, dir_doc}, {"divmod", builtin_divmod, 1, divmod_doc}, {"eval", builtin_eval, 1, eval_doc}, {"execfile", builtin_execfile, 1, execfile_doc}, {"filter", builtin_filter, 1, filter_doc}, - {"float", builtin_float, 1, float_doc}, {"getattr", builtin_getattr, 1, getattr_doc}, {"globals", builtin_globals, 1, globals_doc}, {"hasattr", builtin_hasattr, 1, hasattr_doc}, @@ -2273,14 +1845,11 @@ static PyMethodDef builtin_methods[] = { {"id", builtin_id, 1, id_doc}, {"input", builtin_input, 1, input_doc}, {"intern", builtin_intern, 1, intern_doc}, - {"int", builtin_int, 1, int_doc}, {"isinstance", builtin_isinstance, 1, isinstance_doc}, {"issubclass", builtin_issubclass, 1, issubclass_doc}, {"iter", builtin_iter, 1, iter_doc}, {"len", builtin_len, 1, len_doc}, - {"list", builtin_list, 1, list_doc}, {"locals", builtin_locals, 1, locals_doc}, - {"long", builtin_long, 1, long_doc}, {"map", builtin_map, 1, map_doc}, {"max", builtin_max, 1, max_doc}, {"min", builtin_min, 1, min_doc}, @@ -2296,10 +1865,6 @@ static PyMethodDef builtin_methods[] = { {"round", builtin_round, 1, round_doc}, {"setattr", builtin_setattr, 1, setattr_doc}, {"slice", builtin_slice, 1, slice_doc}, - {"str", builtin_str, 1, str_doc}, - {"tuple", builtin_tuple, 1, tuple_doc}, - {"type", builtin_type, 1, type_doc}, - {"unicode", builtin_unicode, 1, unicode_doc}, {"unichr", builtin_unichr, 1, unichr_doc}, {"vars", builtin_vars, 1, vars_doc}, {"xrange", builtin_xrange, 1, xrange_doc}, @@ -2329,6 +1894,42 @@ _PyBuiltin_Init(void) if (PyDict_SetItemString(dict, "NotImplemented", Py_NotImplemented) < 0) return NULL; + if (PyDict_SetItemString(dict, "classmethod", + (PyObject *) &PyClassMethod_Type) < 0) + return NULL; +#ifndef WITHOUT_COMPLEX + if (PyDict_SetItemString(dict, "complex", + (PyObject *) &PyComplex_Type) < 0) + return NULL; +#endif + if (PyDict_SetItemString(dict, "dictionary", + (PyObject *) &PyDict_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "float", + (PyObject *) &PyFloat_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "int", (PyObject *) &PyInt_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "list", (PyObject *) &PyList_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "long", (PyObject *) &PyLong_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "object", + (PyObject *) &PyBaseObject_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "staticmethod", + (PyObject *) &PyStaticMethod_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "str", (PyObject *) &PyString_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "tuple", + (PyObject *) &PyTuple_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "type", (PyObject *) &PyType_Type) < 0) + return NULL; + if (PyDict_SetItemString(dict, "unicode", + (PyObject *) &PyUnicode_Type) < 0) + return NULL; debug = PyInt_FromLong(Py_OptimizeFlag == 0); if (PyDict_SetItemString(dict, "__debug__", debug) < 0) { Py_XDECREF(debug); diff --git a/Python/ceval.c b/Python/ceval.c index e614e17fc16..e6da9ebe1c3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -13,6 +13,7 @@ #include "frameobject.h" #include "eval.h" #include "opcode.h" +#include "structmember.h" #ifdef macintosh #include "macglue.h" @@ -32,17 +33,7 @@ typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ - -static PyObject *eval_code2(PyCodeObject *, - PyObject *, PyObject *, - PyObject **, int, - PyObject **, int, - PyObject **, int, - PyObject *); - static PyObject *eval_frame(PyFrameObject *); -static char *get_func_name(PyObject *); -static char *get_func_desc(PyObject *); static PyObject *call_object(PyObject *, PyObject *, PyObject *); static PyObject *call_cfunction(PyObject *, PyObject *, PyObject *); static PyObject *call_instance(PyObject *, PyObject *, PyObject *); @@ -98,7 +89,6 @@ static long dxp[256]; #endif #endif - staticforward PyTypeObject gentype; typedef struct { @@ -211,24 +201,11 @@ static struct PyMethodDef gen_methods[] = { {NULL, NULL} /* Sentinel */ }; -static PyObject * -gen_getattr(genobject *gen, char *name) -{ - PyObject *result; - - if (strcmp(name, "gi_frame") == 0) { - result = (PyObject *)gen->gi_frame; - assert(result != NULL); - Py_INCREF(result); - } - else if (strcmp(name, "gi_running") == 0) - result = (PyObject *)PyInt_FromLong((long)gen->gi_running); - else if (strcmp(name, "__members__") == 0) - result = Py_BuildValue("[ss]", "gi_frame", "gi_running"); - else - result = Py_FindMethod(gen_methods, (PyObject *)gen, name); - return result; -} +static struct memberlist gen_memberlist[] = { + {"gi_frame", T_OBJECT, offsetof(genobject, gi_frame), RO}, + {"gi_running", T_INT, offsetof(genobject, gi_running), RO}, + {NULL} /* Sentinel */ +}; statichere PyTypeObject gentype = { PyObject_HEAD_INIT(&PyType_Type) @@ -239,7 +216,7 @@ statichere PyTypeObject gentype = { /* methods */ (destructor)gen_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)gen_getattr, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ @@ -249,7 +226,7 @@ statichere PyTypeObject gentype = { 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ + PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */ @@ -260,6 +237,11 @@ statichere PyTypeObject gentype = { 0, /* tp_weaklistoffset */ (getiterfunc)gen_getiter, /* tp_iter */ (iternextfunc)gen_iternext, /* tp_iternext */ + gen_methods, /* tp_methods */ + gen_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ }; @@ -505,7 +487,7 @@ static int unpack_iterable(PyObject *, int, PyObject **); PyObject * PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) { - return eval_code2(co, + return PyEval_EvalCodeEx(co, globals, locals, (PyObject **)NULL, 0, (PyObject **)NULL, 0, @@ -516,7 +498,7 @@ PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) /* Interpreter main loop */ -PyObject * +static PyObject * eval_frame(PyFrameObject *f) { #ifdef DXPAIRS @@ -965,7 +947,7 @@ eval_frame(PyFrameObject *f) case BINARY_SUBSCR: w = POP(); v = POP(); - if (PyList_Check(v) && PyInt_Check(w)) { + if (v->ob_type == &PyList_Type && PyInt_Check(w)) { /* INLINE: list[int] */ long i = PyInt_AsLong(w); if (i < 0) @@ -2273,8 +2255,8 @@ eval_frame(PyFrameObject *f) return retval; } -static PyObject * -eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, +PyObject * +PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *closure) { @@ -2973,13 +2955,13 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) return NULL; } - result = call_object(func, arg, kw); + result = PyObject_Call(func, arg, kw); Py_DECREF(arg); return result; } /* How often is each kind of object called? The answer depends on the - program. An instrumented call_object() was used to run the Python + program. An instrumented PyObject_Call() was used to run the Python regression test suite. The results were: 4200000 PyCFunctions 390000 fast_function() calls @@ -2992,11 +2974,11 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) most common, but not by such a large margin. */ -static char * -get_func_name(PyObject *func) +char * +PyEval_GetFuncName(PyObject *func) { if (PyMethod_Check(func)) - return get_func_name(PyMethod_GET_FUNCTION(func)); + return PyEval_GetFuncName(PyMethod_GET_FUNCTION(func)); else if (PyFunction_Check(func)) return PyString_AsString(((PyFunctionObject*)func)->func_name); else if (PyCFunction_Check(func)) @@ -3011,8 +2993,8 @@ get_func_name(PyObject *func) } } -static char * -get_func_desc(PyObject *func) +char * +PyEval_GetFuncDesc(PyObject *func) { if (PyMethod_Check(func)) return "()"; @@ -3136,7 +3118,8 @@ call_method(PyObject *func, PyObject *arg, PyObject *kw) PyErr_Format(PyExc_TypeError, "unbound method %s%s must be " "called with instance as first argument", - get_func_name(func), get_func_desc(func)); + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); return NULL; } Py_INCREF(arg); @@ -3199,7 +3182,7 @@ call_eval_code2(PyObject *func, PyObject *arg, PyObject *kw) nk = 0; } - result = eval_code2( + result = PyEval_EvalCodeEx( (PyCodeObject *)PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), (PyObject *)NULL, &PyTuple_GET_ITEM(arg, 0), PyTuple_Size(arg), @@ -3255,7 +3238,7 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) d = &PyTuple_GET_ITEM(argdefs, 0); nd = ((PyTupleObject *)argdefs)->ob_size; } - return eval_code2((PyCodeObject *)co, globals, + return PyEval_EvalCodeEx((PyCodeObject *)co, globals, (PyObject *)NULL, (*pp_stack)-n, na, (*pp_stack)-2*nk, nk, d, nd, closure); @@ -3282,8 +3265,8 @@ update_keyword_args(PyObject *orig_kwdict, int nk, PyObject ***pp_stack, PyErr_Format(PyExc_TypeError, "%.200s%s got multiple values " "for keyword argument '%.200s'", - get_func_name(func), - get_func_desc(func), + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), PyString_AsString(key)); Py_DECREF(key); Py_DECREF(value); @@ -3356,7 +3339,7 @@ do_call(PyObject *func, PyObject ***pp_stack, int na, int nk) callargs = load_args(pp_stack, na); if (callargs == NULL) goto call_fail; - result = call_object(func, callargs, kwdict); + result = PyObject_Call(func, callargs, kwdict); call_fail: Py_XDECREF(callargs); Py_XDECREF(kwdict); @@ -3378,8 +3361,8 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) PyErr_Format(PyExc_TypeError, "%s%s argument after ** " "must be a dictionary", - get_func_name(func), - get_func_desc(func)); + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); goto ext_call_fail; } } @@ -3393,8 +3376,8 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) PyErr_Format(PyExc_TypeError, "%s%s argument after * " "must be a sequence", - get_func_name(func), - get_func_desc(func)); + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); } goto ext_call_fail; } @@ -3411,7 +3394,7 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) callargs = update_star_args(na, nstar, stararg, pp_stack); if (callargs == NULL) goto ext_call_fail; - result = call_object(func, callargs, kwdict); + result = PyObject_Call(func, callargs, kwdict); ext_call_fail: Py_XDECREF(callargs); Py_XDECREF(kwdict); @@ -3632,63 +3615,25 @@ import_all_from(PyObject *locals, PyObject *v) static PyObject * build_class(PyObject *methods, PyObject *bases, PyObject *name) { - int i, n; - if (!PyTuple_Check(bases)) { - PyErr_SetString(PyExc_SystemError, - "build_class with non-tuple bases"); - return NULL; - } - if (!PyDict_Check(methods)) { - PyErr_SetString(PyExc_SystemError, - "build_class with non-dictionary"); - return NULL; - } - if (!PyString_Check(name)) { - PyErr_SetString(PyExc_SystemError, - "build_class with non-string name"); - return NULL; - } - n = PyTuple_Size(bases); - for (i = 0; i < n; i++) { - PyObject *base = PyTuple_GET_ITEM(bases, i); - if (!PyClass_Check(base)) { - /* Call the base's *type*, if it is callable. - This code is a hook for Donald Beaudry's - and Jim Fulton's type extensions. In - unextended Python it will never be triggered - since its types are not callable. - Ditto: call the bases's *class*, if it has - one. This makes the same thing possible - without writing C code. A true meta-object - protocol! */ - PyObject *basetype = (PyObject *)base->ob_type; - PyObject *callable = NULL; - if (PyCallable_Check(basetype)) - callable = basetype; - else - callable = PyObject_GetAttrString( - base, "__class__"); - if (callable) { - PyObject *args; - PyObject *newclass = NULL; - args = Py_BuildValue( - "(OOO)", name, bases, methods); - if (args != NULL) { - newclass = PyEval_CallObject( - callable, args); - Py_DECREF(args); - } - if (callable != basetype) { - Py_DECREF(callable); - } - return newclass; - } - PyErr_SetString(PyExc_TypeError, - "base is not a class object"); - return NULL; + PyObject *metaclass = NULL; + + if (PyDict_Check(methods)) + metaclass = PyDict_GetItemString(methods, "__metaclass__"); + + if (metaclass == NULL) { + if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) + metaclass = (PyObject *) + PyTuple_GET_ITEM(bases, 0)->ob_type; + else { + PyObject *g = PyEval_GetGlobals(); + if (g != NULL && PyDict_Check(g)) + metaclass = PyDict_GetItemString( + g, "__metaclass__"); + if (metaclass == NULL) + metaclass = (PyObject *) &PyClass_Type; } } - return PyClass_New(bases, methods, name); + return PyObject_CallFunction(metaclass, "OOO", name, bases, methods); } static int diff --git a/Python/exceptions.c b/Python/exceptions.c index 214d8e5e623..6fb52ca6c08 100644 --- a/Python/exceptions.c +++ b/Python/exceptions.c @@ -1056,23 +1056,36 @@ static struct { DL_EXPORT(void) -init_exceptions(void) +_PyExc_Init(void) { char *modulename = "exceptions"; int modnamesz = strlen(modulename); int i; + PyObject *me, *mydict, *bltinmod, *bdict, *doc, *args; - PyObject *me = Py_InitModule(modulename, functions); - PyObject *mydict = PyModule_GetDict(me); - PyObject *bltinmod = PyImport_ImportModule("__builtin__"); - PyObject *bdict = PyModule_GetDict(bltinmod); - PyObject *doc = PyString_FromString(module__doc__); - PyObject *args; + me = Py_InitModule(modulename, functions); + if (me == NULL) + goto err; + mydict = PyModule_GetDict(me); + if (mydict == NULL) + goto err; + bltinmod = PyImport_ImportModule("__builtin__"); + if (bltinmod == NULL) + goto err; + bdict = PyModule_GetDict(bltinmod); + if (bdict == NULL) + goto err; + doc = PyString_FromString(module__doc__); + if (doc == NULL) + goto err; - PyDict_SetItemString(mydict, "__doc__", doc); + i = PyDict_SetItemString(mydict, "__doc__", doc); Py_DECREF(doc); - if (PyErr_Occurred()) + if (i < 0) { + err: Py_FatalError("exceptions bootstrapping error."); + return; + } /* This is the base class of all exceptions, so make it first. */ if (make_Exception(modulename) || @@ -1139,7 +1152,7 @@ init_exceptions(void) DL_EXPORT(void) -fini_exceptions(void) +_PyExc_Fini(void) { int i; diff --git a/Python/import.c b/Python/import.c index 82524f654dd..a2106de370d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -43,7 +43,7 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); /* XXX Perhaps the magic number should be frozen and a version field added to the .pyc file header? */ /* New way to come up with the magic number: (YEAR-1995), MONTH, DAY */ -#define MAGIC (60420 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (60717 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the @@ -1968,8 +1968,11 @@ PyImport_Import(PyObject *module_name) } /* Get the __import__ function from the builtins */ - if (PyDict_Check(builtins)) + if (PyDict_Check(builtins)) { import = PyObject_GetItem(builtins, import_str); + if (import == NULL) + PyErr_SetObject(PyExc_KeyError, import_str); + } else import = PyObject_GetAttr(builtins, import_str); if (import == NULL) diff --git a/Python/pythonrun.c b/Python/pythonrun.c index c74b0623bb5..e6162305f47 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -115,6 +115,9 @@ Py_Initialize(void) Py_FatalError("Py_Initialize: can't make first thread"); (void) PyThreadState_Swap(tstate); + if (PyType_InitDict(&PyType_Type) < 0) + Py_FatalError("Py_Initialize: can't initialize 'type'"); + interp->modules = PyDict_New(); if (interp->modules == NULL) Py_FatalError("Py_Initialize: can't make modules dictionary"); @@ -144,7 +147,7 @@ Py_Initialize(void) _PyImport_Init(); /* initialize builtin exceptions */ - init_exceptions(); + _PyExc_Init(); /* phase 2 of builtins */ _PyImport_FixupExtension("__builtin__", "__builtin__"); @@ -238,7 +241,7 @@ Py_Finalize(void) below has been checked to make sure no exceptions are ever raised. */ - fini_exceptions(); + _PyExc_Fini(); /* Delete current thread */ PyInterpreterState_Clear(interp); @@ -1345,7 +1348,7 @@ _Py_AskYesNo(char *prompt) { char buf[256]; - printf("%s [ny] ", prompt); + fprintf(stderr, "%s [ny] ", prompt); if (fgets(buf, sizeof buf, stdin) == NULL) return 0; return buf[0] == 'y' || buf[0] == 'Y';