Most Powerful Open Source ERP

Technical Note on Common Python Pitfalls

Technical Note on common pitfalls when working with Python.
  • Last Update:2016-05-17
  • Version:001
  • Language:en

Your script doesn't behave like expected, though the syntax is correct and code looks sane. Here are some tricky things which can explain its observed behaviour.

Table of Contents

"is" usage

Python "is" keyword gives unexpected results when comparing objects.

  >>> context.foo is context.foo
  False

This is because foo object is wrapped by zope at access in an acquisition wrapper, and that acquisition wrapper is generated at every access. A possible solution if you want to stick to "is" keyword would be

  >>> from Acquisition import aq_base
  >>> aq_base(context.foo) is aq_base(context.foo)
  True

But be reminded that this relies on zope's object cache behaviour: if one day it decides to cache multiple versions of the same object from the point of view of a given thread, this test will fail. Another possibility, if you can access it, is to check for object's oid, but it requires at least a (sub) transaction to have been commited since object was created, and oid is not directly available from restricted environment.

General acquisition warning

When accessing any object, be prepared to get trapped by acquisition. Accessing successfully foo from bar does not mean the foo was the object you were searching for in the first place. This is espacially tricky in categories when the same structure is repeated among multiples "brothers" trees, with some exceptions. For example, the following structure (taken from ERP5Banking unit tests) can easily mislead the programer into thinking his code works:

  • testsite
    • paris
      • caveau
    • caveau
    • siege

Here, accessing testsite/siege/caveau will end up with testsite/caveau object inside an "unusual" acquisition context which will make getParentValue use tricky.

Another example:

  getattr(context.portal_categories.testsite.paris, "another_id", None)

will return None, unless another_id can be acquired from context. In this case, it's recommanded to start from the portal object, using:

  getattr(context.getPortalObject().portal_categories.testsite.paris, "another_id", None)

Related Articles