Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Wednesday, March 20, 2024

Inject class to Python module dynamically

As you probably know in Python after you import some module you may call functions defined in this module. But if you work with more complex scenarios (e.g. when imported module is provided automatically by infrastructure (i.e. you don't control it) or when classes are generated in runtime using Python meta classes) you may face with situation when some class is missing in imported module. In this case Python will show the following error:

AttributeError: module 'SomeExternalModule' has no attribute 'SomeAPI'

(in this example we assume that module name is SomeExternalModule and class name is SomeAPI). Of course if you need functionality defined in missing class you have to resolve this issue properly to ensure that missing class will exist in imported module. But if it is not critical and you just want to pass through these calls you may use trick with injecting class to module dynamically. E.g. class with single static method (method without "self" first argument) can be added to module like that:

def Log(msg):
    print(msg)

SomeExternalModule.SomeAPI = type("SomeAPI", (object, ), {
    "Log": Log
})

Here we injected class SomeAPI to module SomeExternalModule. Class contains one method called Log which just prints message to console. In addition to static methods we may also add instance methods using similar technique - just add "self" first argument to the method in this case.

After that if you will run code which relies on SomeExternalModule.SomeAPI() calls error should gone.

Wednesday, December 16, 2009

Use gmail smtp server for post commit notifications in Subversion via mailer.py

In my previous post I described how to enable conditional post commit notifications in Subversion. In this post I’m going to show how to extend mailer.py from subversion tools in order to use gmail smtp server (smtp.gmail.com:587) for sending emails.

You should note one thing if you want to use gmail smtp server for emailing in your applications – it requires SSL enabled. So first you need to create gmail account – its credentials will be used to connect to smtp.gmail.com.  Once gmail account created you need to add new setting “smtp_use_ssl” in mailer.conf file:

   1: [general]
   2:  
   3: smtp_hostname = smtp.gmail.com:587
   4: smtp_username = your_gmail_login
   5: smtp_password = your_gmail_password
   6:  
   7: # 2009-12-13 asadomov: add custom smtp_use_ssl parameter
   8: smtp_use_ssl = true
   9: ...
Next you need to modify SMTPOutput class in mailer.py in order to use added “smtp_use_ssl” setting:
   1: class SMTPOutput(MailedOutput):
   2:   "Deliver a mail message to an MTA using SMTP."
   3:  
   4:   def start(self, group, params):
   5:     MailedOutput.start(self, group, params)
   6:  
   7:     self.buffer = StringIO()
   8:     self.write = self.buffer.write
   9:  
  10:     self.write(self.mail_headers(group, params))
  11:  
  12:   def finish(self):
  13:     server = smtplib.SMTP(self.cfg.general.smtp_hostname)
  14:  
  15:     # 2009-12-13 asadomov: add ssl configuration (e.g. for gmail smtp server)
  16:     if self.cfg.is_set('general.smtp_use_ssl') and self.cfg.general.smtp_use_ssl.lower() == "true":
  17:       server.ehlo()
  18:       server.starttls()
  19:       server.ehlo()
  20:  
  21:     if self.cfg.is_set('general.smtp_username'):
  22:       server.login(self.cfg.general.smtp_username,
  23:                    self.cfg.general.smtp_password)
  24:     server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue())
  25:     server.quit()

After that you will be able to use gmail smtp server to send post commit notifications via mailer.py.

Sunday, December 13, 2009

Post commit notifications in Subversion on Windows

Recently I needed to extend svn functionality by adding post commit email notifications for small open source project. The idea was to have some possibility to make commit visible for team members without annoying manual emailing (like “please see commit #123 – I added fluent nhibernate configuration”). We wanted to save our time and have some kind of switch for indicating that this commit is public. Another requirement is that we didn’t want all commits public – we wanted to notify only about those commits which were useful for other team members (of course author of commit decided is it useful or not for other :) ).

In order to implement described behavior I needed to make several things:

  1. Add post commit hook in svn server which sends email notifications
  2. Make it possible to send notifications by some condition. We decided to use ‘!’ symbol as indicator that commit is public. I.e. if contributor adds ‘!’ as first symbols in commit description, then notification should be sent (like “!Fluent nhibernate configuration added")

In order to achieve 1st step I decided to use standard mailer.py script which comes from subversion-tools. There were some drawbacks with installing it on windows. So I will describe the process step by step:

  1. Install python 2.6.x on svn server. On the moment when this post is written it is 2.6.4. Note that currently I didn’t not use 3.x version as I didn’t find python bindings for svn for 3.x versions (see below)
  2. Install python bindings from subversion site . On the moment of installing python bindings I had svn 1.6.6 but binsings was only for 1.6.2. Nevertheless it worked on 1.6.6.
  3. Copy mailer.py and mailer.conf into svn server (I copied it into …\repository\hooks\hook-scripts folder). mailer.conf file can be found on the same page with subversion-tools.
  4. Edit mailer.conf. There are many comments in this file which describe mailer configuration. I needed simple solution with sending mails via smtp. So this is stripped configuration file I used:
  5.    1: [general]
       2: smtp_hostname = your_smtp_server
       3: smtp_username = your_smtp_username
       4: smtp_password = your_smtp_password 
       5:  
       6: [defaults] 
       7:  
       8: diff = /usr/bin/diff -u -L %(label_from)s -L %(label_to)s %(from)s %(to)s 
       9:  
      10: commit_subject_prefix = [SVN-Commit]
      11: propchange_subject_prefix =
      12: lock_subject_prefix =
      13: unlock_subject_prefix = 
      14:  
      15: from_addr = your_from_address
      16: to_addr = your_receiver_1@example.com your_receiver_2@example.com [... your_receiver_n@example.com] 
      17:  
      18: reply_to =
      19: generate_diffs = none
      20: show_nonmatching_paths = yes 
      21:  
      22: [maps]

    Note that it is important to have “generate_diffs = none”. Without it you will need to configure diff tool (via “diff” option)

  6. Create post-commit.cmd script in …\repository\hooks folder:
  7.    1: set REPOS=%1
       2: set REV=%2
       3:  
       4: [your_absolute_path_to_repository]/repository/hooks/hook-scripts/mailer.py commit %REPOS% %REV%

    Note that absolute path is used here

After that recipients (specified in “to_addr” option in mailer.conf) will receive notifications after each commit.

Second step is to make these notifications conditional. It will require slightly change of mailer.py script:

image

After that notifications are sent only if contributor typed ‘!’ symbol in commit description.