Requirements and Design for Python Weak Reference Project |
Currently, many Python applications suffer from circular reference problems.
This type of problems usually occurs in the cases when a Python class need to hold references to it’s own methods or properties. Python Win32 programming is a typical example.
These problems could be solved using the garbage collection offered in Python 2.0. But this solution is very expensive. Usually, each application has only few references that cause the circular reference problem. A Java-like weak reference would be a much better solution for this problem.
The following design alternative was considered:
Proxy Pattern – the weak-reference object represents a weak reference that could also be used the same way the original referenced object would be used.
The key here is that the client doesn’t know the difference between the real and the proxy objects.
Unfortunately, the implementation of this pattern would break Python’s object type rules. Two Python objects considered being of the same type, if their respective ob_type members point to the same type object instance. If this rule is not followed, then operations like comparison would not work.
Since the proxy object would have to point to a different object type instance then the referenced Python object, the type identity rules would be broken. This would make it impossible to use the weak-reference objects interchangeably with the referenced Python objects
The author has chosen to implement the requirements using a Weak-Reference Object (WRO) pattern (I came up with this pattern myself)
In this implementation, a weak-reference object holds a non-referenced C pointer to the referenced Python class. The object does not permit direct use of the referenced object. A client code can obtain a new reference to the referenced object from the WRO by calling newref method. There is a possibility that the referenced object has been destroyed by the time a new reference was requested. In this case, the weak-reference object will return a reference to the Python None object.
This architecture does not violate Python’s type identity rules. Also, it forces the users of the weak-reference object to check the value of the returned new reference.
In order for the WR object to track the lifetime of the referenced objects, it needs to intercept the calls to object type’s tp_dealloc method. It does so by replacing the value of the tp_dealloc pointer in the type structure with a pointer to an intercepting dealloc method. (YUCK!!!!). This is the biggest disadvantage of this method. Although it is going to work in 99.9% of the cases, there is still a chance that the type object is located in read-only memory. This would break this implementation of WRO.
The following sequence diagram illustrates the process of creating a new WR object:
The following sequence diagram illustrates the process of obtaining a new reference:
The following sequence diagram illustrates the process of destruction of the referenced object: