(root)/
Python-3.12.0/
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              return Py_NewRef(v);
     159          }
     160          else if (PyErr_Occurred()) {
     161              return NULL;
     162          }
     163      }
     164      return PyObject_GenericGetAttr((PyObject *)self, name);
     165  }
     166  
     167  static int
     168  Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
     169  {
     170      if (self->x_attr == NULL) {
     171          // prepare the attribute dict
     172          self->x_attr = PyDict_New();
     173          if (self->x_attr == NULL) {
     174              return -1;
     175          }
     176      }
     177      if (v == NULL) {
     178          // delete an attribute
     179          int rv = PyDict_DelItem(self->x_attr, name);
     180          if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
     181              PyErr_SetString(PyExc_AttributeError,
     182                  "delete non-existing Xxo attribute");
     183              return -1;
     184          }
     185          return rv;
     186      }
     187      else {
     188          // set an attribute
     189          return PyDict_SetItem(self->x_attr, name, v);
     190      }
     191  }
     192  
     193  /* Xxo methods */
     194  
     195  static PyObject *
     196  Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
     197           PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
     198  {
     199      if (kwnames != NULL && PyObject_Length(kwnames)) {
     200          PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
     201          return NULL;
     202      }
     203      if (nargs != 1) {
     204          PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
     205          return NULL;
     206      }
     207  
     208      PyObject *o = args[0];
     209  
     210      /* Test if the argument is "str" */
     211      if (PyUnicode_Check(o)) {
     212          return Py_NewRef(o);
     213      }
     214  
     215      /* test if the argument is of the Xxo class */
     216      if (PyObject_TypeCheck(o, defining_class)) {
     217          return Py_NewRef(o);
     218      }
     219  
     220      return Py_NewRef(Py_None);
     221  }
     222  
     223  static PyMethodDef Xxo_methods[] = {
     224      {"demo",            _PyCFunction_CAST(Xxo_demo),
     225       METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
     226      {NULL,              NULL}           /* sentinel */
     227  };
     228  
     229  /* Xxo buffer interface */
     230  
     231  static int
     232  Xxo_getbuffer(XxoObject *self, Py_buffer *view, int flags)
     233  {
     234      int res = PyBuffer_FillInfo(view, (PyObject*)self,
     235                                 (void *)self->x_buffer, BUFSIZE,
     236                                 0, flags);
     237      if (res == 0) {
     238          self->x_exports++;
     239      }
     240      return res;
     241  }
     242  
     243  static void
     244  Xxo_releasebuffer(XxoObject *self, Py_buffer *view)
     245  {
     246      self->x_exports--;
     247  }
     248  
     249  static PyObject *
     250  Xxo_get_x_exports(XxoObject *self, void *c)
     251  {
     252      return PyLong_FromSsize_t(self->x_exports);
     253  }
     254  
     255  /* Xxo type definition */
     256  
     257  PyDoc_STRVAR(Xxo_doc,
     258               "A class that explicitly stores attributes in an internal dict");
     259  
     260  static PyGetSetDef Xxo_getsetlist[] = {
     261      {"x_exports", (getter) Xxo_get_x_exports, NULL, NULL},
     262      {NULL},
     263  };
     264  
     265  
     266  static PyType_Slot Xxo_Type_slots[] = {
     267      {Py_tp_doc, (char *)Xxo_doc},
     268      {Py_tp_traverse, Xxo_traverse},
     269      {Py_tp_clear, Xxo_clear},
     270      {Py_tp_finalize, Xxo_finalize},
     271      {Py_tp_dealloc, Xxo_dealloc},
     272      {Py_tp_getattro, Xxo_getattro},
     273      {Py_tp_setattro, Xxo_setattro},
     274      {Py_tp_methods, Xxo_methods},
     275      {Py_bf_getbuffer, Xxo_getbuffer},
     276      {Py_bf_releasebuffer, Xxo_releasebuffer},
     277      {Py_tp_getset, Xxo_getsetlist},
     278      {0, 0},  /* sentinel */
     279  };
     280  
     281  static PyType_Spec Xxo_Type_spec = {
     282      .name = "xxlimited.Xxo",
     283      .basicsize = sizeof(XxoObject),
     284      .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
     285      .slots = Xxo_Type_slots,
     286  };
     287  
     288  
     289  /* Str type definition*/
     290  
     291  static PyType_Slot Str_Type_slots[] = {
     292      {0, 0},  /* sentinel */
     293  };
     294  
     295  static PyType_Spec Str_Type_spec = {
     296      .name = "xxlimited.Str",
     297      .basicsize = 0,
     298      .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     299      .slots = Str_Type_slots,
     300  };
     301  
     302  
     303  /* Function of two integers returning integer (with C "long int" arithmetic) */
     304  
     305  PyDoc_STRVAR(xx_foo_doc,
     306  "foo(i,j)\n\
     307  \n\
     308  Return the sum of i and j.");
     309  
     310  static PyObject *
     311  xx_foo(PyObject *module, PyObject *args)
     312  {
     313      long i, j;
     314      long res;
     315      if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
     316          return NULL;
     317      res = i+j; /* XXX Do something here */
     318      return PyLong_FromLong(res);
     319  }
     320  
     321  
     322  /* Function of no arguments returning new Xxo object */
     323  
     324  static PyObject *
     325  xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
     326  {
     327      XxoObject *rv;
     328  
     329      rv = newXxoObject(module);
     330      if (rv == NULL)
     331          return NULL;
     332      return (PyObject *)rv;
     333  }
     334  
     335  
     336  
     337  /* List of functions defined in the module */
     338  
     339  static PyMethodDef xx_methods[] = {
     340      {"foo",             xx_foo,         METH_VARARGS,
     341          xx_foo_doc},
     342      {"new",             xx_new,         METH_NOARGS,
     343          PyDoc_STR("new() -> new Xx object")},
     344      {NULL,              NULL}           /* sentinel */
     345  };
     346  
     347  
     348  /* The module itself */
     349  
     350  PyDoc_STRVAR(module_doc,
     351  "This is a template module just for instruction.");
     352  
     353  static int
     354  xx_modexec(PyObject *m)
     355  {
     356      xx_state *state = PyModule_GetState(m);
     357  
     358      state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
     359      if (state->Error_Type == NULL) {
     360          return -1;
     361      }
     362      if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
     363          return -1;
     364      }
     365  
     366      state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
     367      if (state->Xxo_Type == NULL) {
     368          return -1;
     369      }
     370      if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
     371          return -1;
     372      }
     373  
     374      // Add the Str type. It is not needed from C code, so it is only
     375      // added to the module dict.
     376      // It does not inherit from "object" (PyObject_Type), but from "str"
     377      // (PyUnincode_Type).
     378      PyObject *Str_Type = PyType_FromModuleAndSpec(
     379          m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
     380      if (Str_Type == NULL) {
     381          return -1;
     382      }
     383      if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
     384          return -1;
     385      }
     386      Py_DECREF(Str_Type);
     387  
     388      return 0;
     389  }
     390  
     391  static PyModuleDef_Slot xx_slots[] = {
     392      {Py_mod_exec, xx_modexec},
     393      {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
     394      {0, NULL}
     395  };
     396  
     397  static int
     398  xx_traverse(PyObject *module, visitproc visit, void *arg)
     399  {
     400      xx_state *state = PyModule_GetState(module);
     401      Py_VISIT(state->Xxo_Type);
     402      Py_VISIT(state->Error_Type);
     403      return 0;
     404  }
     405  
     406  static int
     407  xx_clear(PyObject *module)
     408  {
     409      xx_state *state = PyModule_GetState(module);
     410      Py_CLEAR(state->Xxo_Type);
     411      Py_CLEAR(state->Error_Type);
     412      return 0;
     413  }
     414  
     415  static struct PyModuleDef xxmodule = {
     416      PyModuleDef_HEAD_INIT,
     417      .m_name = "xxlimited",
     418      .m_doc = module_doc,
     419      .m_size = sizeof(xx_state),
     420      .m_methods = xx_methods,
     421      .m_slots = xx_slots,
     422      .m_traverse = xx_traverse,
     423      .m_clear = xx_clear,
     424      /* m_free is not necessary here: xx_clear clears all references,
     425       * and the module state is deallocated along with the module.
     426       */
     427  };
     428  
     429  
     430  /* Export function for the module (*must* be called PyInit_xx) */
     431  
     432  PyMODINIT_FUNC
     433  PyInit_xxlimited(void)
     434  {
     435      return PyModuleDef_Init(&xxmodule);
     436  }