Restrictive APIs for Python

Discussion in 'Python' started by Will Ware, Dec 15, 2006.

  1. Will Ware

    Will Ware Guest

    Python has no inherent provision for a restrictive API that blocks
    accesses to methods and variables outside an allowed set.
    Inexperienced Python programmers may fail to adhere to an agreed-upon
    API, directly accessing the private internals of a class. Adherence to
    defined APIs is a good thing. This function allows a class to specify
    its API, and raise AttributeErrors for disallowed accesses.

    def restrictiveApi(klas):
    class newklas:
    def __init__(self, *args):
    self.__inst = apply(klas, args)
    def __getattr__(self, attr):
    # If the attribute is in the permitted API, then return
    # the correct thing, no matter who asks for it.
    #
    if attr in self.__inst._PUBLIC:
    return getattr(self.__inst, attr)
    # If the attribute is outside the permitted API, then
    # return it only if the calling class is in the list of
    # friends. Otherwise raise an AttributeError.
    #
    elif hasattr(self.__inst, '_FRIENDS'):
    # find the class of the method that called us
    try:
    raise Exception
    except:
    import sys
    tb = sys.exc_info()[2]
    callerClass = tb.tb_frame.f_back.\
    f_locals['self'].__class__
    # if it's a friend class, return the requested thing
    if callerClass.__name__ in self.__inst._FRIENDS:
    return getattr(self.__inst, attr)
    # if not a friend, raise an AttributeError
    raise AttributeError, attr
    return newklas

    To use this, a class needs to define two class variables, _PUBLIC and
    _FRIENDS, both being lists (or tuples) of strings. The _PUBLIC list
    gives the names of all methods and variables that should be considered
    public, i.e. any other class may use them. The _FRIENDS list gives the
    names of classes that are allowed free access to all methods and
    variables in the protected class. The _FRIENDS list is optional.

    Having defined _PUBLIC and optionally _FRIENDS, use something like the
    following to protect your class. Restricting the API will incur a
    performance overhead, so it's best to do it under the control of some
    sort of debug flag.

    if debug_flag:
    from restrictive import restrictiveApi
    MyClass = restrictiveApi(MyClass)

    ======== Examples ==========

    class ClassUnderTest:
    # This class has a private variable called privateX. It can be
    # set using the setX() method or gotten using the x() method.
    # If another class appears in the _FRIENDS list, that class
    # can access privateX directly.
    #
    _PUBLIC = ('x', 'setX')
    _FRIENDS = ('FriendClass',)
    def __init__(self, x): # __init__ is always callable by anybody
    self.setX(x)
    def x(self): # callable by anybody
    return self.privateX
    def setX(self, x): # callable by anybody
    self.privateX = x

    ClassUnderTest = restrictiveApi(ClassUnderTest)

    class FriendClass:
    def getX(self, cut):
    return cut.privateX # this works fine

    class StrangerClass:
    def getX(self, cut):
    return cut.privateX # this raises an AttributeError
     
    Will Ware, Dec 15, 2006
    #1
    1. Advertising

  2. On 15 dic, 11:31, "Will Ware" <> wrote:
    > Python has no inherent provision for a restrictive API that blocks
    > accesses to methods and variables outside an allowed set.
    > Inexperienced Python programmers may fail to adhere to an agreed-upon
    > API, directly accessing the private internals of a class.


    In Python, the usual way of saying "don't play with me" is prepending
    an underscore: _private
    BTW, have you *ever* tested your code?

    --
    Gabriel Genellina
     
    Gabriel Genellina, Dec 15, 2006
    #2
    1. Advertising

  3. Will Ware wrote:
    > Python has no inherent provision for a restrictive API that blocks
    > accesses to methods and variables outside an allowed set.
    > Inexperienced Python programmers may fail to adhere to an agreed-upon
    > API, directly accessing the private internals of a class.


    Just don't document those private internals.
    Or document that they must not be accessed directly.

    --
    Roberto Bonvallet
     
    Roberto Bonvallet, Dec 15, 2006
    #3
  4. Will Ware

    Will Ware Guest

    Gabriel Genellina wrote:
    > In Python, the usual way of saying "don't play with me" is prepending
    > an underscore: _private


    Thanks, I am familiar with that.

    > BTW, have you *ever* tested your code?


    Yes, we have a QA process. The problem is not that the code doesn't
    work, it does. It was developed by a mix of more and less experienced
    programmers, and early in the code's history, some hadn't been trained
    on the benefits of complying to an API, or Pythonic idioms like the
    leading underscore. So the code varies in its clarity, and some
    maintenance chores aren't as pleasant as they might be.

    I have found that the work of more experienced programmers often
    includes improving the quality of code written by less experienced
    programmers. Is this inconsistent with your own experience?
     
    Will Ware, Dec 15, 2006
    #4
  5. On 15 dic, 13:46, "Will Ware" <> wrote:
    > Gabriel Genellina wrote:
    > > In Python, the usual way of saying "don't play with me" is prepending
    > > an underscore: _private

    > Thanks, I am familiar with that.

    So enforce it instead of going against the nature of the language.

    > > BTW, have you *ever* tested your code?

    > Yes, we have a QA process.
    > The problem is not that the code doesn't
    > work, it does.


    Does it?

    >>> c = ClassUnderTest(1)
    >>> print c

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 22, in __getattr__
    KeyError: 'self'
    >>>


    Even if you try to correct this, c is no more an instance of the
    intended class, it's a "newklas" now, so isinstance() wont work
    anymore. It has lost his docstring so you don't know its purpose and
    intended usage anymore; you cannot inspect its methods using dir() so
    editors and other tools can't help you writting and documenting code.
    And a lot of other things upon which a lot of Python programs relies
    on.
    Don't go *against* the language, learn to use it the right way on your
    own advantage.

    > It was developed by a mix of more and less experienced
    > programmers, and early in the code's history, some hadn't been trained
    > on the benefits of complying to an API, or Pythonic idioms like the
    > leading underscore. So the code varies in its clarity, and some
    > maintenance chores aren't as pleasant as they might be.

    You can use tools like pylint or pychecker to try to detect and fix
    those issues.

    > I have found that the work of more experienced programmers often
    > includes improving the quality of code written by less experienced
    > programmers. Is this inconsistent with your own experience?

    If the less experienced guys get the feedback, I feel that's Ok.
    Just cut them a finger phalanx each time someone breaks the rules and
    see how well your programmers behave :)

    --
    Gabriel Genellina
     
    Gabriel Genellina, Dec 15, 2006
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Vijay Kumar R Zanvar

    Most restrictive type

    Vijay Kumar R Zanvar, May 28, 2004, in forum: C Programming
    Replies:
    6
    Views:
    431
    Dan Pop
    Jun 2, 2004
  2. Replies:
    1
    Views:
    17,126
  3. Replies:
    4
    Views:
    102
    Peter Scott
    Aug 5, 2008
  4. A. Farber
    Replies:
    9
    Views:
    123
  5. Stevo
    Replies:
    11
    Views:
    207
    Stevo
    Jul 24, 2008
Loading...

Share This Page