diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2008-08-09 04:35:06 +0000 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2008-08-09 04:35:06 +0000 |
commit | 72f3132d2b98a44881ae7001d0001602a66bf8b5 (patch) | |
tree | d75ce61572ba4d18fcc746583f911f150e1c968a | |
parent | 3495c67d57868a02bafe6f1935d4846cd5615bf5 (diff) | |
download | freeipa.git-72f3132d2b98a44881ae7001d0001602a66bf8b5.tar.gz freeipa.git-72f3132d2b98a44881ae7001d0001602a66bf8b5.tar.xz freeipa.git-72f3132d2b98a44881ae7001d0001602a66bf8b5.zip |
95: Improved docstrings for ReadOnly class; added ReadOnly.__islocked__() method; added corresponding unit tests
-rw-r--r-- | ipalib/plugable.py | 39 | ||||
-rw-r--r-- | ipalib/tests/test_plugable.py | 12 |
2 files changed, 51 insertions, 0 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py index c5ec08ec..4ce4a9ba 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -39,6 +39,39 @@ def check_identifier(name): class ReadOnly(object): """ Base class for classes with read-only attributes. + + Be forewarned that Python does not offer true read-only user defined + classes. In particular, do not rely upon the read-only-ness of this + class for security purposes. + + The point of this class is not to make it impossible to set or delete + attributes, but do make it impossible to accidentally do so. The plugins + are not thread-safe: in the server, they are loaded once and the same + instances will be used to process many requests. Therefore, it is + imperative that they not set any instance attributes after they have + been initialized. This base class enforces that policy. + + For example: + + >>> class givenName(ReadOnly): + >>> def __init__(self): + >>> self.whatever = 'some value' # Hasn't been locked yet + >>> self.__lock__() + >>> + >>> def finalize(self, api): + >>> # After the instance has been locked, attributes can still be + >>> # set, but only in a round-about, unconventional way: + >>> object.__setattr__(self, 'api', api) + >>> + >>> def normalize(self, value): + >>> # After the instance has been locked, trying to set an + >>> # attribute in the normal way will raise AttributeError. + >>> self.value = value # Not thread safe! + >>> return self.actually_normalize() + >>> + >>> def actually_normalize(self): + >>> # Again, this is not thread safe: + >>> return unicode(self.value).strip() """ __locked = False @@ -50,6 +83,12 @@ class ReadOnly(object): assert self.__locked is False, '__lock__() can only be called once' self.__locked = True + def __islocked__(self): + """ + Returns True if this instance is locked, False otherwise. + """ + return self.__locked + def __setattr__(self, name, value): """ Raises an AttributeError if ReadOnly.__lock__() has already been called; diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py index 605debe5..42453ed5 100644 --- a/ipalib/tests/test_plugable.py +++ b/ipalib/tests/test_plugable.py @@ -62,6 +62,18 @@ class test_ReadOnly(ClassChecker): def test_class(self): assert self.cls.__bases__ == (object,) assert callable(self.cls.__lock__) + assert callable(self.cls.__islocked__) + + def test_lock(self): + """ + Tests the `__lock__` and `__islocked__` methods. + """ + o = self.cls() + assert o.__islocked__() is False + o.__lock__() + assert o.__islocked__() is True + raises(AssertionError, o.__lock__) # Can only be locked once + assert o.__islocked__() is True # This should still be True def test_when_unlocked(self): """ |