Example: Using the Python smtplib.SMTP class to send mail from within Eiffel

Sending SMTP mail is a relatively straightforward task for many users, and Python provides a module and class for doing just that. Eiffel, unfortunately, does not, and for this example we leave it to the PYTHON_USER_CLASS class and the make_eiffel_stub.py script to do the bulk of the work of using the Python smtplib.SMTP module from within Eiffel.

To do so and make things easier, we do add a few Make targets to the project's makefile:

Example 9-4. Makefile targets for generating the classes

#PYTHON and SCRIPTPATH are initialized elsewhere to point to
#the Python interpreter and the path to the ePolyglot scripts
GENERATED_EIFFEL_FILES = smtplib_smtp.e
#generated by the ePolyglot script "make_eiffel_stub.py"
$(GENERATED_EIFFEL_FILES) : 
        $(PYTHON) $(SCRIPTPATH)/make_eiffel_stub.py smtplib SMTP > smtplib_smtp.e

This way, the Eiffel source file can be generated each time the project is made from scratch, which simplifies the build a touch and means that the automatically-generated class file is always current with the Python class in question.

The source for our sample program is below:

Example 9-5. The mail_test program

class MAIL_TEST
--a simple class designed to send a quick mail message out.
--using the python smtplib.SMTP library class
inherit
   PYTHON_CLIENT_WRAPPER
   
creation
   make
   
feature
   
   usage_text : STRING is "Usage:%N%Tmail_test from-address to-%
   %address message-text%N%N"
   
   joined_strings( strings : FIXED_ARRAY[STRING]; delimiter : STRING ) : STRING is
	 --create a string made of all the strings in 'strings' 
	 --separated by the delimiter
      local
	 i : INTEGER
      do
	 create Result.make( 0 )
	 from 
	    i := strings.lower
	 until
	    not strings.valid_index( i )
	 loop
	    Result.append( strings.item( i ) )
	    i := i + 1
	    if strings.valid_index( i ) then
	       Result.append( delimiter )
	    end
	 end
      end
   
   from_address : STRING is
      do
	 Result := argument( 1 )
      end
   
   to_address : STRING is
      do
	 Result := argument( 2 )
      end
   
   message : STRING is
      do
	 Result := joined_strings( command_arguments.slice( 3, command_arguments.upper ), " " )
      end
   
   make is
      local
	 r : PYTHON_OBJECT
	 smtp : SMTPLIB_SMTP
      do
	 py_initialize
	 create smtp.from_init( Void )
	 if argument_count < 3 then
	    std_output.put_string( usage_text )
	 else
	    std_output.put_string( "Sending message:%NFrom: " + from_address + "%NTo: " +
				   to_address + "%NMessage: " + message + "%N" ) 
	    r := smtp.connect( <<>> )
	    r := smtp.sendmail( << from_address, to_address, message >> )
	    r := smtp.quit
	 end 
      rescue
	 if not py_err_occurred.is_null then
	    py_err_print
	 end
      end
   
end
   

Which, when executed, gives results similar to the following:

Example 9-6. Sample output from mail_test

[vputz@yak_prime smtp]$ ./mail_test
Usage:
	mail_test from-address to-address message-text

[vputz@yak_prime smtp]$ ./mail_test vputz vputz@localhost This is a test
Sending message:
From: vputz
To: vputz@localhost
Message: This is a test
[vputz@yak_prime smtp]$ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/spool/mail/vputz": 1 message 1 new
>N  1 vputz@yak_prime.wing  Tue Aug 15 16:29  11/436  
& 1
Message 1:
From vputz@yak_prime.wingedyak.com  Tue Aug 15 16:29:01 2000
Date: Tue, 15 Aug 2000 16:29:00 -0600
From: "Victor B. Putz" <vputz@yak_prime.wingedyak.com>

This is a test

& d 1
& q
[vputz@yak_prime smtp]$