Additional added value--the PYTHON_CLIENT class

While the SWIG-generated PYTHON_CLIENT_WRAPPER class did expose much of the Python/C API, there were a number of slightly higher-level features we desired, mostly having to do with minor error checking and creation of Python primitive objects. While this could have been done with a mixin class, we decided that the easiest way to deal with it was to create a descendent of PYTHON_CLIENT_WRAPPER named PYTHON_CLIENT, and add some functionality to it.

Error checking. Most of the Python/C API functions, like many C functions, return a success/error code as a return value. In C, programmers are free to ignore the error code as they wish--a debatable decision, but one which is impossible in Eiffel. If the Eiffel compiler sees that you are using a function with a return value, some variable had better be there to accept the return value, as failure to do so generates a compiler error!

While this is not a bad thing, it did mean that client code was creating a lot of temporary variables. Since only one Python function was likely to be in use at a time, it seemed a better design choice to include helper functions around both the integer results and the pointer results which could be generated by Python functions:

Example 6-6. Python return value helpers in PYTHON_CLIENT

   last_python_integer_result : INTEGER 
	 --stores the last integer result of a call to the Python API 
	 ----note that this will be replicated for all descendants
   
   last_python_integer_result_ok : BOOLEAN is
	 --did last C call to Python API return an error condition?
      do
	 Result := ( last_python_integer_result /= -1 )
      end
   
   last_python_pointer_result : POINTER
	 --stores the last pointer result of a call to the Python API
	 --note that this will be replicated for all descendants
   
   last_python_pointer_result_ok : BOOLEAN is
	 --did last pointer call to Python API return an error condition?
      do
	 Result := ( last_python_pointer_result.is_not_null )
      end
   

Client code, then, could use the functionality like so:

Example 6-7. Use of the Python return value helpers: feature PYTHON_OBJECT.del_attr_string

   del_attr_string( attribute_name : STRING ) is
      do
	 last_python_integer_result := py_object_del_attr_string( python_object_pointer, attribute_name )
      ensure
	 last_python_integer_result_ok
      end

This is a fairly naive way to handle erroneous return values, but it does make program flow very straightforward and eliminates the use of temporaries in every single function which uses the Python/C API. We considered moving all such 'error checking' into the PYTHON_CLIENT class in the form of an invariant, but decided that was too restrictive--it prevented clients from being able to accept erroneous return values and act upon them.

Other functionality was later added to the PYTHON_CLIENT class--a class-wide PYTHON_OBJECT_FACTORY class to handle creation of primitive Python objects and handle data conversion, some helpers to get the python global and local definitions, a wrapper around py_initialize (which may be expanded to fill in arguments), a wrapper to easily create Python primitives via the interpreter, and a wrapper which creates a SWIG-compatible pointer string from an Eiffel object. Many of these will be discussed further in the documentation writeup.