Document Actions
09/29/2005
YADI, part 3: timedtest, a decorator for time performance tests
Grig Gheorghiu has released a pretty cool tool, called pyUnitPerf. It's a
port of Mike Clark's JUnitPerf.

This tool runs your tests cases, and measure the time they take to run. It
can pop a Failure when the tests lasts too long. It also has a Load runner,
but nothing comparable to the amazing funkload tool

Thats quite useful in some cases, when your test fixture does not make the
code run slower, like it happens in ZopeTestCase.

So since pyUnitPerf associates a max time with a TestCase class, and since
test fixture can slow down the tests, it makes it really hard to use it, to
measure a distinct test method.

So I have taken back the idea and put it in a decorator that I can use on
all test methods I want to control, performances wise.   

The timedtest decorator


import unittest
import time

# might be changed to allow tolerance
# if tests are run on other boxes
TOLERANCE = 0.05

class DurationError(AssertionError): pass

def timedtest(max_time, tolerance=TOLERANCE):
    """ timedtest decorator
    decorates the test method with a timer
    when the time spent by the test exceeds
    max_time in seconds, an Assertion error is thrown.
    """
    def _timedtest(function):
        def wrapper(*args, **kw):
            start_time = time.time()
            try:
                function(*args, **kw)
            finally:
                total_time = time.time() - start_time
                if total_time > max_time + tolerance:
                    raise DurationError(('Test was too long (%.2f s)'
                                           % total_time))
        return wrapper

    return _timedtest
    

Usage


It's quite easy to use, just decorate a TestCase method with it, giving the max time allowed for the test, in seconds:    

class MyTestCase(unittest.TestCase):

    def __init__(self, name):
        unittest.TestCase.__init__(self, name)

    @timedtest(2)
    def test_timecritical(self):
        time.sleep(1.5)

    @timedtest(1)
    def test_timecritical2(self):
        time.sleep(1.5)

if __name__ == '__main__':
    suite = unittest.makeSuite(MyTestCase)
    unittest.TextTestRunner().run(suite)

Now this can help much to make sure some critical code does not get too
  slow.
  

[tziade@Tarek Desktop]$ python timedtest.py
.F
======================================================================
FAIL: test_timecritical2 (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "timedtest.py", line 28, in wrapper
    raise DurationError(('Test was too long (%.2f s)'
DurationError: Test was too long (1.50 s)

----------------------------------------------------------------------
Ran 2 tests in 2.999s

 
Posted by Tarek Ziadé @ 09/29/2005 01:22 AM. - Categories: python -  0 comments
09/25/2005
wikimailing

I am thinking about a feature that could link portal knowledge in a very simple way, about the same way CPSSharedCalendar and CPSSubscriptions does when they send emails.

I keep on trying to find such features in existing tool, it has to exists.. Whenever I find it, i'll remove this stupid blog entry ;)

Well, here's what I've taught of:

  • Provided that the portal members are actually using the webmail to talk to each other about their work,
  • Provided that users are gathered in work groups, and therefore share a common knowledge:

Mails beeing sent for this private circle could have meta content, by using wiki notation, the webmail could then parse these mail bodies to display internal links to wikipages or to future wikipages.

This feature would also make the mail writing easier, since it can refers to common knowledge, and consolidate this knowledge because it is the central part of all dialogs and therefore all important infos,out of the dialog, are kept centralized.

One wiki per work group should be the perfect balance.

Here's a sample:

Regular mail exchanges :
Hi Bob,

Can we meet sometimes about the x32 project, 
do you remember about it ? 

I sent you a mail 4 months ago about it.

you have the specs here: http://xxx/specs 
 and the client web page is there: http://xxx/dsddd

Please let me know when we can meet about it,
and if you have more info about them, let me know. 

Bill


Hi bill,

Sorry, don't remember,  can't find your mail.
what was the subject ?

OK, anway, i have looked at the pages (the second link is down, though)
I have aslo found infos here: http://xxx/dsdsdssd

let's meet on tuesday

Bob
WikiMailing:
Hi Bob,
Can we meet sometimes about the [x32 project], 
Please let me know when we can meet about it, 
 don't hesitate to complete the informations we have. 

Bill 


Hi bill,

OK, i have completed them.

let's meet on tuesday, please create the meeting 
 page by clicking on the link below and fill it. thx

[x32 project meeting]?

Bob
CPSMailAccess could then parse the mail body, according to the user center of interests (ie: the groups he belongs to) and make links to different wiki groups.

OK, this is what we all tend to do. For example Trac links are heavily used in mails when we talk about bug or features, but, if we talk about other subjects, the principal knowledge base moves in the best cases from trac to another tool, but most of the time we have to look over our mails to recollect some infos...

,

The main idea is to pour email info richness into a centralized, common, base, and to make the mail beeing just what it should be: a simple trigger.


Posted by Tarek Ziadé @ 09/25/2005 02:51 AM. - Categories: semantic_web -  0 comments
09/24/2005
YADI, part 2: a simple Psyco decorator
Psyco is a pretty cool tool from Armin Rigo, that can really speed up the code in some cases. It relies on generating machine code by writing the corresponding bytes directly into executable memory.

For language lawyers and people interested in the subject, the website has a lot of papers on how Psyco works.

But the caveats makes it quite unusable as a  global application directive and it's better to target the code where we want Psyco to do the speed.

Psyco comes with a handy set of API, and the proxy() function, that takes a callable object and returns a psychoed-callable object, makes it easy to create a decorator that can be used whenever needed.

import psyco

# decorator psycoed 
def psycoed(function): 
    try: 
        return psyco.proxy(function) 
    except TypeError: 
        return function 
The TypeError thing just prevents errors on objets that can't be proxied.

The psyco-effect is quite cool:

def normal():
    a = 0
    for i in range(5000):
        a = a + 3
    return a

@psycoed
def speedy():
    a = 0
    for i in range(5000):
        a = a + 3
    return a

if __name__ == '__main__':
    import timeit

    temps = timeit.Timer('speedy()', 'from __main__ import speedy').timeit(10000)
    print 'psycoed: %f s' % temps

    temps = timeit.Timer('normal()', 'from __main__ import normal').timeit(10000)
    print 'not psycoed: %f s' % temps

[...]

[tziade@Tarek Desktop]$ python psycote.py
psycoed: 0.249526 s
not psycoed: 14.255591 s
Posted by Tarek Ziadé @ 09/24/2005 11:14 PM. - Categories: python -  0 comments
09/23/2005
YADI, part 1: Memento Pattern

YADI ?


"YADI" stands for Yet Another Design pattern Implementation, because GoF design patterns have been reviewed and implemented a bunch of people already, all over the web. I'd like to present my own implementation for some of them, though, in this blog.

This first post is about Memento, inspired from Zoran Isailovski's one on ASPN, (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413838) but with a slightly different approach to simplify its use.

Memento: What for ?


Memento DP says: a state of the program data can be saved and reloaded, thus allowing to make safe transactionnal operations. In other words, some program data changes can be rolled back at some point, or commited.

The code pattern that describes it the best is:

begin_transaction()
try:
  ...
except:
  rollback_transaction()
  raise
else:
  commit_transaction()

Depending on what the program does, the scope of data that are to be saved can vary a lot. Transactions in RDBMS covers the whole database for instance.

The easiest way to implement Memento in Python is to set the transactional level to objects.

And the easiest way to implement this is to create a decorator that allow some methods of an object to become transactionnal.

The transaction decorator


The decorator transaction could be:

import copy

def get_memento(object):
  """ get the object state """
  return copy.deepcopy(object.__dict__)

def set_memento(object, state):
  """ restore the objet """
  object.__dict__.clear()
  object.__dict__.update(state)

def transaction(method):
  """ decorator """
  def bind(object, *args, **kw):
    state = get_memento(object)
    try:
      return method(object, *args, **kw)
    except:
      set_memento(object, state)
      raise

  return bind

This implementation is based on the copy module, that works with the __dict__ atribute of the object, thus it won't work if the object is a new-style class that has dropped the use of __dict__ in some ways (slots, descriptors, etc..).

Anyway, it's pretty usefull on most classes, and they can implement the __deepcopy__ method to control the copy behavior.

Example of use


The example below implements a run() method, that becomes transactionnal

class M(object):
  def __init__(self):
    def o():
      print 'OK'
    self.a = 12
    self.b = ['a', 32]
    self.l = o

    @transaction
    def run(self):
      self.b.append('c')
      self.o = 12
      self.a = '14'
      self.a += 1

objet = M()

try:
  objet.run()
except TypeError:
  pass

print objet.a
print objet.b
objet.l()

[...]

[tziade@Tarek Desktop]$ python memento.py
12
['a', 32]
OK

When run() fails, because a + 1 (a is a string) leads to a TypeError, the state of M is automatically rolled back.
Posted by Tarek Ziadé @ 09/23/2005 02:16 PM. - Categories: python -  0 comments
Last modified: 01/25/2005 06:15 PM

Nuxeo Bloggers: Log in!
Nuxeo - Indesko - Nuxeo 5 Project
All content is copyrighted by their author.
CPSSkins is Copyright © 2003-2006 by Jean-Marc Orliaguet. | CPS is Copyright © 2002-2006 by Nuxeo SAS.