[Python-checkins] python/dist/src/Doc/ext newtypes.tex, 1.36, 1.37 noddy4.c, 1.1, 1.2 noddy2.c, 1.4, 1.5 noddy3.c, 1.4, 1.5

dcjim at users.sourceforge.net dcjim at users.sourceforge.net
Wed Jul 14 21:07:27 CEST 2004


Update of /cvsroot/python/python/dist/src/Doc/ext
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15472/Doc/ext

Modified Files:
	newtypes.tex noddy4.c noddy2.c noddy3.c 
Log Message:
Updated documentation to:

- point out the importance of reassigning data members before
  assigning thier values

- correct my missconception about return values from visitprocs. Sigh.

- mention the labor saving Py_VISIT and Py_CLEAR macros.


Index: newtypes.tex
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/newtypes.tex,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** newtypes.tex	6 Jun 2004 15:59:18 -0000	1.36
--- newtypes.tex	14 Jul 2004 19:07:24 -0000	1.37
***************
*** 240,245 ****
  
  \begin{verbatim}
! import noddy
! mynoddy = noddy.Noddy()
  \end{verbatim}
  
--- 240,245 ----
  
  \begin{verbatim}
! >>> import noddy
! >>> mynoddy = noddy.Noddy()
  \end{verbatim}
  
***************
*** 383,387 ****
  values were \NULL, we could have used \cfunction{PyType_GenericNew()} as
  our new method, as we did before.  \cfunction{PyType_GenericNew()}
! initializes all of the instance variable members to NULLs.
  
  The new method is a static method that is passed the type being
--- 383,387 ----
  values were \NULL, we could have used \cfunction{PyType_GenericNew()} as
  our new method, as we did before.  \cfunction{PyType_GenericNew()}
! initializes all of the instance variable members to \NULL.
  
  The new method is a static method that is passed the type being
***************
*** 408,412 ****
  such subclasses without getting a \exception{TypeError}.)}
  
- 
  We provide an initialization function:
  
--- 408,411 ----
***************
*** 415,419 ****
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
--- 414,418 ----
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL, *tmp;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
***************
*** 425,437 ****
  
      if (first) {
!         Py_XDECREF(self->first);
          Py_INCREF(first);
          self->first = first;
      }
  
      if (last) {
!         Py_XDECREF(self->last);
          Py_INCREF(last);
          self->last = last;
      }
  
--- 424,438 ----
  
      if (first) {
!         tmp = self->first;
          Py_INCREF(first);
          self->first = first;
+         Py_XDECREF(tmp);
      }
  
      if (last) {
!         tmp = self->last;
          Py_INCREF(last);
          self->last = last;
+         Py_XDECREF(tmp);
      }
  
***************
*** 454,457 ****
--- 455,496 ----
  positional and keyword arguments.
  
+ Initializers can be called multiple times.  Anyone can call the
+ \method{__init__()} method on our objects.  For this reason, we have
+ to be extra careful when assigning the new values.  We might be
+ tempted, for example to assign the \member{first} member like this:
+ 
+ \begin{verbatim}
+     if (first) {
+         Py_XDECREF(self->first);
+         Py_INCREF(first);
+         self->first = first;
+     }
+ \end{verbatim}
+ 
+ But this would be risky.  Our type doesn't restrict the type of the
+ \member{first} member, so it could be any kind of object.  It could
+ have a destructor that causes code to be executed that tries to
+ access the \member{first} member.  To be paranoid and protect
+ ourselves against this possibility, we almost always reassign members
+ before decrementing their reference counts.  When don't we have to do
+ this?
+ \begin{itemize}
+ \item when we absolutely know that the reference count is greater than
+   1
+ \item when we know that deallocation of the object\footnote{This is
+   true when we know that the object is a basic type, like a string or
+   a float} will not cause any
+   calls back into our type's code
+ \item when decrementing a reference count in a \member{tp_dealloc}
+   handler when garbage-collections is not supported\footnote{We relied
+   on this in the \member{tp_dealloc} handler in this example, because
+   our type doesn't support garbage collection. Even if a type supports
+   garbage collection, there are calls that can be made to ``untrack''
+   the object from garbage collection, however, these calls are
+   advanced and not covered here.}
+ \item 
+ \end{itemize}
+ 
+ 
  We want to want to expose our instance variables as attributes. There
  are a number of ways to do that. The simplest way is to define member
***************
*** 683,686 ****
--- 722,764 ----
  \end{verbatim}
  
+ We also need to update the \member{tp_init} handler to only allow
+ strings\footnote{We now know that the first and last members are strings,
+ so perhaps we could be less careful about decrementing their
+ reference counts, however, we accept instances of string subclasses.
+ Even though deallocating normal strings won't call back into our
+ objects, we can't guarantee that deallocating an instance of a string
+ subclass won't. call back into out objects.} to be passed:
+ 
+ \begin{verbatim}
+ static int
+ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
+ {
+     PyObject *first=NULL, *last=NULL, *tmp;
+ 
+     static char *kwlist[] = {"first", "last", "number", NULL};
+ 
+     if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, 
+                                       &first, &last, 
+                                       &self->number))
+         return -1; 
+ 
+     if (first) {
+         tmp = self->first;
+         Py_INCREF(first);
+         self->first = first;
+         Py_DECREF(tmp);
+     }
+ 
+     if (last) {
+         tmp = self->last;
+         Py_INCREF(last);
+         self->last = last;
+         Py_DECREF(tmp);
+     }
+ 
+     return 0;
+ }
+ \end{verbatim}
+ 
  With these changes, we can assure that the \member{first} and
  \member{last} members are never NULL so we can remove checks for \NULL
***************
*** 714,719 ****
  In the second version of the \class{Noddy} example, we allowed any
  kind of object to be stored in the \member{first} or \member{last}
! attributes. This means that \class{Noddy} objects can participate in
! cycles:
  
  \begin{verbatim}
--- 792,799 ----
  In the second version of the \class{Noddy} example, we allowed any
  kind of object to be stored in the \member{first} or \member{last}
! attributes\footnote{Even in the third version, we aren't guaranteed to
! avoid cycles.  Instances of string subclasses are allowed and string
! subclasses could allow cycles even if normal strings don't.}. This
! means that \class{Noddy} objects can participate in cycles:
  
  \begin{verbatim}
***************
*** 738,745 ****
  Noddy_traverse(Noddy *self, visitproc visit, void *arg)
  {
!     if (self->first && visit(self->first, arg) < 0)
!         return -1;
!     if (self->last && visit(self->last, arg) < 0)
!         return -1;
  
      return 0;
--- 818,833 ----
  Noddy_traverse(Noddy *self, visitproc visit, void *arg)
  {
!     int vret;
! 
!     if (self->first) {
!         vret = visit(self->first, arg);
!         if (vret != 0)
!             return vret;
!     }
!     if (self->last) {
!         vret = visit(self->last, arg);
!         if (vret != 0)
!             return vret;
!     }
  
      return 0;
***************
*** 750,754 ****
  \cfunction{visit()} function, which is passed to the traversal method.
  The \cfunction{visit()} function takes as arguments the subobject and
! the extra argument \var{arg} passed to the traversal method.
  
  We also need to provide a method for clearing any subobjects that can
--- 838,859 ----
  \cfunction{visit()} function, which is passed to the traversal method.
  The \cfunction{visit()} function takes as arguments the subobject and
! the extra argument \var{arg} passed to the traversal method.  It
! returns an integer value that must be returned if it is non-zero.  
! 
! 
! Python 2.4 and higher provide a \cfunction{Py_VISIT()} that automates
! calling visit functions.  With \cfunction{Py_VISIT()}, the
! \cfunction{Noddy_traverse()} can be simplified:
! 
! 
! \begin{verbatim}
! static int
! Noddy_traverse(Noddy *self, visitproc visit, void *arg)
! {
!     Py_VISIT(self->first);
!     Py_VISIT(self->last);
!     return 0;
! }
! \end{verbatim}
  
  We also need to provide a method for clearing any subobjects that can
***************
*** 760,767 ****
  Noddy_clear(Noddy *self)
  {
!     Py_XDECREF(self->first);
      self->first = NULL;
!     Py_XDECREF(self->last);
      self->last = NULL;
  
      return 0;
--- 865,877 ----
  Noddy_clear(Noddy *self)
  {
!     PyObject *tmp;
! 
!     tmp = self->first;
      self->first = NULL;
!     Py_XDECREF(tmp);
! 
!     tmp = self->last;
      self->last = NULL;
+     Py_XDECREF(tmp);
  
      return 0;
***************
*** 776,779 ****
--- 886,916 ----
  \end{verbatim}
  
+ Notice the use of a temporary variable in \cfunction{Noddy_clear()}.
+ We use the temporary variable so that we can set each member to \NULL
+ before decrementing it's reference count.  We do this because, as was
+ discussed earlier, if the reference count drops to zero, we might
+ cause code to run that calls back into the object.  In addition,
+ because we now support garbage collection, we also have to worry about
+ code being run that triggers garbage collection.  If garbage
+ collection is run, our \member{tp_traverse} handler could get called.
+ We can't take a chance of having \cfunction{Noddy_traverse()} called
+ when a member's reference count has dropped to zero and it's value
+ hasn't been set to \NULL.
+ 
+ Python 2.4 and higher provide a \cfunction{Py_CLEAR()} that automates
+ the careful decrementing of reference counts.  With
+ \cfunction{Py_CLEAR()}, the \cfunction{Noddy_clear()} function can be
+ simplified:
+ 
+ \begin{verbatim}
+ static int 
+ Noddy_clear(Noddy *self)
+ {
+     Py_CLEAR(self->first);
+     Py_CLEAR(self->last);
+     return 0;
+ }
+ \end{verbatim}
+ 
  Finally, we add the \constant{Py_TPFLAGS_HAVE_GC} flag to the class
  flags:
***************
*** 807,811 ****
  they are defined in the structure, because there is a lot of
  historical baggage that impacts the ordering of the fields; be sure
! your type initializaion keeps the fields in the right order!  It's
  often easiest to find an example that includes all the fields you need
  (even if they're initialized to \code{0}) and then change the values
--- 944,948 ----
  they are defined in the structure, because there is a lot of
  historical baggage that impacts the ordering of the fields; be sure
! your type initialization keeps the fields in the right order!  It's
  often easiest to find an example that includes all the fields you need
  (even if they're initialized to \code{0}) and then change the values
***************
*** 825,829 ****
  
  These fields tell the runtime how much memory to allocate when new
! objects of this type are created.  Python has some builtin support
  for variable length structures (think: strings, lists) which is where
  the \member{tp_itemsize} field comes in.  This will be dealt with
--- 962,966 ----
  
  These fields tell the runtime how much memory to allocate when new
! objects of this type are created.  Python has some built-in support
  for variable length structures (think: strings, lists) which is where
  the \member{tp_itemsize} field comes in.  This will be dealt with
***************
*** 836,840 ****
  Here you can put a string (or its address) that you want returned when
  the Python script references \code{obj.__doc__} to retrieve the
! docstring.
     
  Now we come to the basic type methods---the ones most extension types
--- 973,977 ----
  Here you can put a string (or its address) that you want returned when
  the Python script references \code{obj.__doc__} to retrieve the
! doc string.
     
  Now we come to the basic type methods---the ones most extension types
***************
*** 916,920 ****
  In Python, there are three ways to generate a textual representation
  of an object: the \function{repr()}\bifuncindex{repr} function (or
! equivalent backtick syntax), the \function{str()}\bifuncindex{str}
  function, and the \keyword{print} statement.  For most objects, the
  \keyword{print} statement is equivalent to the \function{str()}
--- 1053,1057 ----
  In Python, there are three ways to generate a textual representation
  of an object: the \function{repr()}\bifuncindex{repr} function (or
! equivalent back-tick syntax), the \function{str()}\bifuncindex{str}
  function, and the \keyword{print} statement.  For most objects, the
  \keyword{print} statement is equivalent to the \function{str()}
***************
*** 984,988 ****
  likely want to write to that file object.
  
! Here is a sampe print function:
  
  \begin{verbatim}
--- 1121,1125 ----
  likely want to write to that file object.
  
! Here is a sample print function:
  
  \begin{verbatim}
***************
*** 1139,1146 ****
  An interesting advantage of using the \member{tp_members} table to
  build descriptors that are used at runtime is that any attribute
! defined this way can have an associated docstring simply by providing
  the text in the table.  An application can use the introspection API
  to retrieve the descriptor from the class object, and get the
! docstring using its \member{__doc__} attribute.
  
  As with the \member{tp_methods} table, a sentinel entry with a
--- 1276,1283 ----
  An interesting advantage of using the \member{tp_members} table to
  build descriptors that are used at runtime is that any attribute
! defined this way can have an associated doc string simply by providing
  the text in the table.  An application can use the introspection API
  to retrieve the descriptor from the class object, and get the
! doc string using its \member{__doc__} attribute.
  
  As with the \member{tp_methods} table, a sentinel entry with a
***************
*** 1287,1291 ****
  indicate that the slots are present and should be checked by the
  interpreter.  (The flag bit does not indicate that the slot values are
! non-\NULL. The flag may be set to indicate the presense of a slot,
  but a slot may still be unfilled.)
  
--- 1424,1428 ----
  indicate that the slots are present and should be checked by the
  interpreter.  (The flag bit does not indicate that the slot values are
! non-\NULL. The flag may be set to indicate the presence of a slot,
  but a slot may still be unfilled.)
  
***************
*** 1310,1314 ****
  
  This function, if you choose to provide it, should return a hash
! number for an instance of your datatype. Here is a moderately
  pointless example:
  
--- 1447,1451 ----
  
  This function, if you choose to provide it, should return a hash
! number for an instance of your data type. Here is a moderately
  pointless example:
  
***************
*** 1328,1333 ****
  \end{verbatim}
  
! This function is called when an instance of your datatype is "called",
! for example, if \code{obj1} is an instance of your datatype and the Python
  script contains \code{obj1('hello')}, the \member{tp_call} handler is
  invoked.
--- 1465,1470 ----
  \end{verbatim}
  
! This function is called when an instance of your data type is "called",
! for example, if \code{obj1} is an instance of your data type and the Python
  script contains \code{obj1('hello')}, the \member{tp_call} handler is
  invoked.
***************
*** 1337,1341 ****
  \begin{enumerate}
    \item
!     \var{arg1} is the instance of the datatype which is the subject of
      the call. If the call is \code{obj1('hello')}, then \var{arg1} is
      \code{obj1}.
--- 1474,1478 ----
  \begin{enumerate}
    \item
!     \var{arg1} is the instance of the data type which is the subject of
      the call. If the call is \code{obj1('hello')}, then \var{arg1} is
      \code{obj1}.
***************
*** 1431,1435 ****
  
  In order to learn how to implement any specific method for your new
! datatype, do the following: Download and unpack the Python source
  distribution.  Go the \file{Objects} directory, then search the
  C source files for \code{tp_} plus the function you want (for
--- 1568,1572 ----
  
  In order to learn how to implement any specific method for your new
! data type, do the following: Download and unpack the Python source
  distribution.  Go the \file{Objects} directory, then search the
  C source files for \code{tp_} plus the function you want (for

Index: noddy4.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/noddy4.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** noddy4.c	28 Jun 2003 13:29:16 -0000	1.1
--- noddy4.c	14 Jul 2004 19:07:24 -0000	1.2
***************
*** 12,19 ****
  Noddy_traverse(Noddy *self, visitproc visit, void *arg)
  {
!     if (self->first && visit(self->first, arg) < 0)
!         return -1;
!     if (self->last && visit(self->last, arg) < 0)
!         return -1;
  
      return 0;
--- 12,27 ----
  Noddy_traverse(Noddy *self, visitproc visit, void *arg)
  {
!     int vret;
! 
!     if (self->first) {
!         vret = visit(self->first, arg);
!         if (vret != 0)
!             return vret;
!     }
!     if (self->last) {
!         vret = visit(self->last, arg);
!         if (vret != 0)
!             return vret;
!     }
  
      return 0;
***************
*** 23,30 ****
  Noddy_clear(Noddy *self)
  {
!     Py_XDECREF(self->first);
      self->first = NULL;
!     Py_XDECREF(self->last);
      self->last = NULL;
  
      return 0;
--- 31,43 ----
  Noddy_clear(Noddy *self)
  {
!     PyObject *tmp;
! 
!     tmp = self->first;
      self->first = NULL;
!     Py_XDECREF(tmp);
! 
!     tmp = self->last;
      self->last = NULL;
+     Py_XDECREF(tmp);
  
      return 0;
***************
*** 68,72 ****
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
--- 81,85 ----
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL, *tmp;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
***************
*** 78,90 ****
  
      if (first) {
!         Py_XDECREF(self->first);
          Py_INCREF(first);
          self->first = first;
      }
  
      if (last) {
!         Py_XDECREF(self->last);
          Py_INCREF(last);
          self->last = last;
      }
  
--- 91,105 ----
  
      if (first) {
!         tmp = self->first;
          Py_INCREF(first);
          self->first = first;
+         Py_XDECREF(tmp);
      }
  
      if (last) {
!         tmp = self->last;
          Py_INCREF(last);
          self->last = last;
+         Py_XDECREF(tmp);
      }
  

Index: noddy2.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/noddy2.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** noddy2.c	28 Jun 2003 11:53:47 -0000	1.4
--- noddy2.c	14 Jul 2004 19:07:24 -0000	1.5
***************
*** 47,51 ****
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
--- 47,51 ----
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL, *tmp;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
***************
*** 57,69 ****
  
      if (first) {
!         Py_XDECREF(self->first);
          Py_INCREF(first);
          self->first = first;
      }
  
      if (last) {
!         Py_XDECREF(self->last);
          Py_INCREF(last);
          self->last = last;
      }
  
--- 57,71 ----
  
      if (first) {
!         tmp = self->first;
          Py_INCREF(first);
          self->first = first;
+         Py_XDECREF(tmp);
      }
  
      if (last) {
!         tmp = self->last;
          Py_INCREF(last);
          self->last = last;
+         Py_XDECREF(tmp);
      }
  

Index: noddy3.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/noddy3.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** noddy3.c	28 Jun 2003 11:54:03 -0000	1.4
--- noddy3.c	14 Jul 2004 19:07:24 -0000	1.5
***************
*** 47,55 ****
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
  
!     if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, 
                                        &first, &last, 
                                        &self->number))
--- 47,55 ----
  Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
  {
!     PyObject *first=NULL, *last=NULL, *tmp;
  
      static char *kwlist[] = {"first", "last", "number", NULL};
  
!     if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, 
                                        &first, &last, 
                                        &self->number))
***************
*** 57,69 ****
  
      if (first) {
!         Py_DECREF(self->first);
          Py_INCREF(first);
          self->first = first;
      }
  
      if (last) {
!         Py_DECREF(self->last);
          Py_INCREF(last);
          self->last = last;
      }
  
--- 57,71 ----
  
      if (first) {
!         tmp = self->first;
          Py_INCREF(first);
          self->first = first;
+         Py_DECREF(tmp);
      }
  
      if (last) {
!         tmp = self->last;
          Py_INCREF(last);
          self->last = last;
+         Py_DECREF(tmp);
      }
  



More information about the Python-checkins mailing list