(root)/
Python-3.11.7/
Modules/
xxlimited.c
       1  /* Use this file as a template to start implementing a module that
       2     also declares object types. All occurrences of 'Xxo' should be changed
       3     to something reasonable for your objects. After that, all other
       4     occurrences of 'xx' should be changed to something reasonable for your
       5     module. If your module is named foo your source file should be named
       6     foo.c or foomodule.c.
       7  
       8     You will probably want to delete all references to 'x_attr' and add
       9     your own types of attributes instead.  Maybe you want to name your
      10     local variables other than 'self'.  If your object type is needed in
      11     other files, you'll have to create a file "foobarobject.h"; see
      12     floatobject.h for an example.
      13  
      14     This module roughly corresponds to::
      15  
      16        class Xxo:
      17           """A class that explicitly stores attributes in an internal dict"""
      18  
      19            def __init__(self):
      20                # In the C class, "_x_attr" is not accessible from Python code
      21                self._x_attr = {}
      22                self._x_exports = 0
      23  
      24            def __getattr__(self, name):
      25                return self._x_attr[name]
      26  
      27            def __setattr__(self, name, value):
      28                self._x_attr[name] = value
      29  
      30            def __delattr__(self, name):
      31                del self._x_attr[name]
      32  
      33            @property
      34            def x_exports(self):
      35                """Return the number of times an internal buffer is exported."""
      36                # Each Xxo instance has a 10-byte buffer that can be
      37                # accessed via the buffer interface (e.g. `memoryview`).
      38                return self._x_exports
      39  
      40            def demo(o, /):
      41                if isinstance(o, str):
      42                    return o
      43                elif isinstance(o, Xxo):
      44                    return o
      45                else:
      46                    raise Error('argument must be str or Xxo')
      47  
      48        class Error(Exception):
      49            """Exception raised by the xxlimited module"""
      50  
      51        def foo(i: int, j: int, /):
      52            """Return the sum of i and j."""
      53            # Unlike this pseudocode, the C function will *only* work with
      54            # integers and perform C long int arithmetic
      55            return i + j
      56  
      57        def new():
      58            return Xxo()
      59  
      60        def Str(str):
      61            # A trivial subclass of a built-in type
      62            pass
      63     */
      64  
      65  #define Py_LIMITED_API 0x030b0000
      66  
      67  #include "Python.h"
      68  #include <string.h>
      69  
      70  #define BUFSIZE 10
      71  
      72  // Module state
      73  typedef struct {
      74      PyObject *Xxo_Type;    // Xxo class
      75      PyObject *Error_Type;       // Error class
      76  } xx_state;
      77  
      78  
      79  /* Xxo objects */
      80  
      81  // Instance state
      82  typedef struct {
      83      PyObject_HEAD
      84      PyObject            *x_attr;           /* Attributes dictionary */
      85      char                x_buffer[BUFSIZE]; /* buffer for Py_buffer */
      86      Py_ssize_t          x_exports;         /* how many buffer are exported */
      87  } XxoObject;
      88  
      89  // XXX: no good way to do this yet
      90  // #define XxoObject_Check(v)      Py_IS_TYPE(v, Xxo_Type)
      91  
      92  static XxoObject *
      93  newXxoObject(PyObject *module)
      94  {
      95      xx_state *state = PyModule_GetState(module);
      96      if (state == NULL) {
      97          return NULL;
      98      }
      99      XxoObject *self;
     100      self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
     101      if (self == NULL) {
     102          return NULL;
     103      }
     104      self->x_attr = NULL;
     105      memset(self->x_buffer, 0, BUFSIZE);
     106      self->x_exports = 0;
     107      return self;
     108  }
     109  
     110  /* Xxo finalization */
     111  
     112  static int
     113  Xxo_traverse(PyObject *self_obj, visitproc visit, void *arg)
     114  {
     115      // Visit the type
     116      Py_VISIT(Py_TYPE(self_obj));
     117  
     118      // Visit the attribute dict
     119      XxoObject *self = (XxoObject *)self_obj;
     120      Py_VISIT(self->x_attr);
     121      return 0;
     122  }
     123  
     124  static int
     125  Xxo_clear(XxoObject *self)
     126  {
     127      Py_CLEAR(self->x_attr);
     128      return 0;
     129  }
     130  
     131  static void
     132  Xxo_finalize(PyObject *self_obj)
     133  {
     134      XxoObject *self = (XxoObject *)self_obj;
     135      Py_CLEAR(self->x_attr);
     136  }
     137  
     138  static void
     139  Xxo_dealloc(PyObject *self)
     140  {
     141      PyObject_GC_UnTrack(self);
     142      Xxo_finalize(self);
     143      PyTypeObject *tp = Py_TYPE(self);
     144      freefunc free = PyType_GetSlot(tp, Py_tp_free);
     145      free(self);
     146      Py_DECREF(tp);
     147  }
     148  
     149  
     150  /* Xxo attribute handling */
     151  
     152  static PyObject *
     153  Xxo_getattro(XxoObject *self, PyObject *name)
     154  {
     155      if (self->x_attr != NULL) {
     156          PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
     157          if (v != NULL) {
     158              Py_INCREF(v);
     159              return v;
     160          }
     161          else if (PyErr_Occurred()) {
     162              return NULL;
     163          }
     164      }
     165      return PyObject_GenericGetAttr((PyObject *)self, name);
     166  }
     167  
     168  static int
     169  Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
     170  {
     171      if (self->x_attr == NULL) {
     172          // prepare the attribute dict
     173          self->x_attr = PyDict_New();
     174          if (self->x_attr == NULL) {
     175              return -1;
     176          }
     177      }
     178      if (v == NULL) {
     179          // delete an attribute
     180          int rv = PyDict_DelItem(self->x_attr, name);
     181          if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
     182              PyErr_SetString(PyExc_AttributeError,
     183                  "delete non-existing Xxo attribute");
     184              return -1;
     185          }
     186          return rv;
     187      }
     188      else {
     189          // set an attribute
     190          return PyDict_SetItem(self->x_attr, name, v);
     191      }
     192  }
     193  
     194  /* Xxo methods */
     195  
     196  static PyObject *
     197  Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
     198           PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
     199  {
     200      if (kwnames != NULL && PyObject_Length(kwnames)) {
     201          PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
     202          return NULL;
     203      }
     204      if (nargs != 1) {
     205          PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
     206          return NULL;
     207      }
     208  
     209      PyObject *o = args[0];
     210  
     211      /* Test if the argument is "str" */
     212      if (PyUnicode_Check(o)) {
     213          Py_INCREF(o);
     214          return o;
     215      }
     216  
     217      /* test if the argument is of the Xxo class */
     218      if (PyObject_TypeCheck(o, defining_class)) {
     219          Py_INCREF(o);
     220          return o;
     221      }
     222  
     223      Py_INCREF(Py_None);
     224      return Py_None;
     225  }
     226  
     227  static PyMethodDef Xxo_methods[] = {
     228      {"demo",            _PyCFunction_CAST(Xxo_demo),
     229       METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
     230      {NULL,              NULL}           /* sentinel */
     231  };
     232  
     233  /* Xxo buffer interface */
     234  
     235  static int
     236  Xxo_getbuffer(XxoObject *self, Py_buffer *view, int flags)
     237  {
     238      int res = PyBuffer_FillInfo(view, (PyObject*)self,
     239                                 (void *)self->x_buffer, BUFSIZE,
     240                                 0, flags);
     241      if (res == 0) {
     242          self->x_exports++;
     243      }
     244      return res;
     245  }
     246  
     247  static void
     248  Xxo_releasebuffer(XxoObject *self, Py_buffer *view)
     249  {
     250      self->x_exports--;
     251  }
     252  
     253  static PyObject *
     254  Xxo_get_x_exports(XxoObject *self, void *c)
     255  {
     256      return PyLong_FromSsize_t(self->x_exports);
     257  }
     258  
     259  /* Xxo type definition */
     260  
     261  PyDoc_STRVAR(Xxo_doc,
     262               "A class that explicitly stores attributes in an internal dict");
     263  
     264  static PyGetSetDef Xxo_getsetlist[] = {
     265      {"x_exports", (getter) Xxo_get_x_exports, NULL, NULL},
     266      {NULL},
     267  };
     268  
     269  
     270  static PyType_Slot Xxo_Type_slots[] = {
     271      {Py_tp_doc, (char *)Xxo_doc},
     272      {Py_tp_traverse, Xxo_traverse},
     273      {Py_tp_clear, Xxo_clear},
     274      {Py_tp_finalize, Xxo_finalize},
     275      {Py_tp_dealloc, Xxo_dealloc},
     276      {Py_tp_getattro, Xxo_getattro},
     277      {Py_tp_setattro, Xxo_setattro},
     278      {Py_tp_methods, Xxo_methods},
     279      {Py_bf_getbuffer, Xxo_getbuffer},
     280      {Py_bf_releasebuffer, Xxo_releasebuffer},
     281      {Py_tp_getset, Xxo_getsetlist},
     282      {0, 0},  /* sentinel */
     283  };
     284  
     285  static PyType_Spec Xxo_Type_spec = {
     286      .name = "xxlimited.Xxo",
     287      .basicsize = sizeof(XxoObject),
     288      .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
     289      .slots = Xxo_Type_slots,
     290  };
     291  
     292  
     293  /* Str type definition*/
     294  
     295  static PyType_Slot Str_Type_slots[] = {
     296      {0, 0},  /* sentinel */
     297  };
     298  
     299  static PyType_Spec Str_Type_spec = {
     300      .name = "xxlimited.Str",
     301      .basicsize = 0,
     302      .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     303      .slots = Str_Type_slots,
     304  };
     305  
     306  
     307  /* Function of two integers returning integer (with C "long int" arithmetic) */
     308  
     309  PyDoc_STRVAR(xx_foo_doc,
     310  "foo(i,j)\n\
     311  \n\
     312  Return the sum of i and j.");
     313  
     314  static PyObject *
     315  xx_foo(PyObject *module, PyObject *args)
     316  {
     317      long i, j;
     318      long res;
     319      if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
     320          return NULL;
     321      res = i+j; /* XXX Do something here */
     322      return PyLong_FromLong(res);
     323  }
     324  
     325  
     326  /* Function of no arguments returning new Xxo object */
     327  
     328  static PyObject *
     329  xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
     330  {
     331      XxoObject *rv;
     332  
     333      rv = newXxoObject(module);
     334      if (rv == NULL)
     335          return NULL;
     336      return (PyObject *)rv;
     337  }
     338  
     339  
     340  
     341  /* List of functions defined in the module */
     342  
     343  static PyMethodDef xx_methods[] = {
     344      {"foo",             xx_foo,         METH_VARARGS,
     345          xx_foo_doc},
     346      {"new",             xx_new,         METH_NOARGS,
     347          PyDoc_STR("new() -> new Xx object")},
     348      {NULL,              NULL}           /* sentinel */
     349  };
     350  
     351  
     352  /* The module itself */
     353  
     354  PyDoc_STRVAR(module_doc,
     355  "This is a template module just for instruction.");
     356  
     357  static int
     358  xx_modexec(PyObject *m)
     359  {
     360      xx_state *state = PyModule_GetState(m);
     361  
     362      state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
     363      if (state->Error_Type == NULL) {
     364          return -1;
     365      }
     366      if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
     367          return -1;
     368      }
     369  
     370      state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
     371      if (state->Xxo_Type == NULL) {
     372          return -1;
     373      }
     374      if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
     375          return -1;
     376      }
     377  
     378      // Add the Str type. It is not needed from C code, so it is only
     379      // added to the module dict.
     380      // It does not inherit from "object" (PyObject_Type), but from "str"
     381      // (PyUnincode_Type).
     382      PyObject *Str_Type = PyType_FromModuleAndSpec(
     383          m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
     384      if (Str_Type == NULL) {
     385          return -1;
     386      }
     387      if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
     388          return -1;
     389      }
     390      Py_DECREF(Str_Type);
     391  
     392      return 0;
     393  }
     394  
     395  static PyModuleDef_Slot xx_slots[] = {
     396      {Py_mod_exec, xx_modexec},
     397      {0, NULL}
     398  };
     399  
     400  static int
     401  xx_traverse(PyObject *module, visitproc visit, void *arg)
     402  {
     403      xx_state *state = PyModule_GetState(module);
     404      Py_VISIT(state->Xxo_Type);
     405      Py_VISIT(state->Error_Type);
     406      return 0;
     407  }
     408  
     409  static int
     410  xx_clear(PyObject *module)
     411  {
     412      xx_state *state = PyModule_GetState(module);
     413      Py_CLEAR(state->Xxo_Type);
     414      Py_CLEAR(state->Error_Type);
     415      return 0;
     416  }
     417  
     418  static struct PyModuleDef xxmodule = {
     419      PyModuleDef_HEAD_INIT,
     420      .m_name = "xxlimited",
     421      .m_doc = module_doc,
     422      .m_size = sizeof(xx_state),
     423      .m_methods = xx_methods,
     424      .m_slots = xx_slots,
     425      .m_traverse = xx_traverse,
     426      .m_clear = xx_clear,
     427      /* m_free is not necessary here: xx_clear clears all references,
     428       * and the module state is deallocated along with the module.
     429       */
     430  };
     431  
     432  
     433  /* Export function for the module (*must* be called PyInit_xx) */
     434  
     435  PyMODINIT_FUNC
     436  PyInit_xxlimited(void)
     437  {
     438      return PyModuleDef_Init(&xxmodule);
     439  }