Example: Using Python primitives from within Eiffel

So how well do our new Python primitive classes hold up, and how easily can they be used? The following sample program uses the new hierarchy for a simple demonstration of many of the PYTHON_OBJECT classes:

Example 7-1. The "primitives_test.e" sample program

class PRIMITIVES_TEST
   
inherit
   PYTHON_CLIENT
      
creation
   make

feature
   
   do_string_demo is
	 --demonstrate use of python string primitives
      local
	 s : PYTHON_STRING
      do
	 std_output.put_string( "Python strings can be created from %
                                %Eiffel strings, and vice versa.%N" )
	 std_output.put_string( "Creating Python string 'Hello, World!'%N" )
	 create s.from_string( "Hello, World!" )
	 std_output.put_string( "Created string: " + s.to_string + "%N%N")
      end
   
   do_number_demo is
	 --demonstrate use of python number primitives
      local
	 a, b, c : PYTHON_INTEGER
	 af, bf : PYTHON_FLOAT
      do
	 std_output.put_string( "Python numbers can be created from %
                                %Eiffel numbers, and%Ncan be %
                                %manipulated in the same ways:%N" )
	 create a.from_integer( 5 )
	 create b.from_integer( 6 )
	 create c.from_integer( 10 )
	 std_output.put_string( "5+6+10 = " )
	 std_output.put_integer( (a + b + c).to_integer )
	 std_output.put_new_line
	 std_output.put_string( "10 / 5 = " )
	 std_output.put_integer( ( c / a ).to_integer )
	 std_output.put_new_line
	 std_output.put_string( "10 / 6 = " )
	 std_output.put_integer( ( c / b ).to_integer )
	 std_output.put_new_line
	 std_output.put_string( "Remainder of 10 / 6 : " )
	 std_output.put_integer( (c \\ b).to_integer )
	 std_output.put_new_line
	 --similar acts with floating point
	 create af.from_double( 2.5 )
	 create bf.from_double( 6.353 )
	 std_output.put_string( "2.5 + 6.353 = " )
	 std_output.put_double( ( af + bf ).to_double )
	 std_output.put_new_line
	 std_output.put_string( "6.353 / 2.5 = " )
	 std_output.put_double( ( bf / af ).to_double )
	 std_output.put_new_line
	 std_output.put_new_line	 
      end
   
   do_list_demo is
	 --demonstrate use of Python lists
      local
	 a : PYTHON_LIST
      do
	 create a.make
	 std_output.put_string( "Python lists can be created easily, %
                                %and support many of the same%
                                %%Nfeatures as Eiffel arrays...but not all.%N" )
	 std_output.put_string( "Creating a list of strings 'Hello', 'Eiffel'%
                                % 'world!'%N" )
	 a.append( python_object_factory.new_python_string( "Hello" ) )
	 a.append( python_object_factory.new_python_string( "Eiffel" ) )
	 a.append( python_object_factory.new_python_string( "World!" ) )
	 std_output.put_string( "Python representation: " + a.to_string + "%N" )
	 std_output.put_string( "Replacing element 1:%N" )
	 a.put( python_object_factory.new_python_string( "Python" ), 1 )
	 std_output.put_string( "Python representation: " + a.to_string + "%N" )
	 std_output.put_string( "Inserting 'or Eiffel' after 'Python':%N" )
	 a.insert( python_object_factory.new_python_string( "or" ), 2 )
	 a.insert( python_object_factory.new_python_string( "Eiffel" ), 3 )	 
	 std_output.put_string( "Python representation: " + a.to_string + "%N" )
	 std_output.put_string( "Taking or setting slices is also possible.%N%N" )
      end
   
   do_dictionary_demo is
	 --demonstrate use of Python dictionaries
      local
	 a : PYTHON_DICTIONARY
      do
	 create a.make
	 std_output.put_string( "Python dictionaries can be used to %
                                %implement mappings.%N" )
	 std_output.put_string( "creating the simple dictionary { 'a': 'Hello',%
                                % 'b' : 'World!' }%N" )
	 a.set_item_string( "b", python_object_factory.new_python_string( "World!" ) )
	 a.set_item_string( "a", python_object_factory.new_python_string( "Hello" ) )
	 std_output.put_string( "Python representation: " + a.to_string + "%N" )
	 std_output.put_string( "Keys: " + a.keys.to_string + "%N" )
	 std_output.put_string( "Values: " + a.values.to_string + "%N" )
	 std_output.put_string( "Value of a: " + a.get_item_string( "a" ).to_string + "%N")
      end
   
   make is
      do
	 py_initialize
	 do_string_demo
	 do_number_demo
	 do_list_demo
	 do_dictionary_demo
      rescue
	 if not py_err_occurred.is_null then
	    py_err_print
	 end
      end
   
end
   

While voluminous, it is designed to showcase many of the features of the PYTHON_OBJECT hierarchy: object creation and use, manipulation of objects from within Eiffel, and general ease of use. Unfortunately, any time you mix other languages or libraries with Eiffel, you lose some of the simplicity of the creation process, and so the details of making this simple example are a bit confusing, involving rules for SWIG and the addition of some C code to the program's compilation line. For more details of the make process, examine the files production/examples/examples_common/boilerplate.mk.in and production/examples/primitives/Makefile.in. While the Makefile is only slightly complex, the above source file was the only actual code written to take advantage of the embedded Python interpreter. When run, it produces the following output:

Example 7-2. Output from the "primitives_test.e" sample program

Python strings can be created from Eiffel strings, and vice versa.
Creating Python string 'Hello, World!'
Created string: Hello, World!

Python numbers can be created from Eiffel numbers, and
can be manipulated in the same ways:
5+6+10 = 21 
10 / 5 = 2
10 / 6 = 1
Remainder of 10 / 6 : 4
2.5 + 6.353 = 8.853000
6.353 / 2.5 = 2.541200

Python lists can be created easily, and support many of the same
features as Eiffel arrays...but not all.
Creating a list of strings 'Hello', 'Eiffel' 'world!'
Python representation: ['Hello', 'Eiffel', 'World!']
Replacing element 1:
Python representation: ['Hello', 'Python', 'World!']
Inserting 'or Eiffel' after 'Python':
Python representation: ['Hello', 'Python', 'or', 'Eiffel', 'World!']
Taking or setting slices is also possible.

Python dictionaries can be used to implement mappings.
creating the simple dictionary { 'a': 'Hello', 'b' : 'World!' }
Python representation: {'b': 'World!', 'a': 'Hello'}
Keys: ['b', 'a']
Values: ['World!', 'Hello']
Value of a: 'Hello'