Restrictive APIs for Python

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Will Ware

    #1

    Restrictive APIs for Python

    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__(sel f, 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._PU BLIC:
    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_b ack.\
    f_locals['self'].__class__
    # if it's a friend class, return the requested thing
    if callerClass.__n ame__ in self.__inst._FR IENDS:
    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

  • Gabriel Genellina

    #2
    Re: Restrictive APIs for Python

    On 15 dic, 11:31, "Will Ware" <will.w...@gmai l.comwrote:
    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

    Comment

    • Roberto Bonvallet

      #3
      Re: Restrictive APIs for Python

      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

      Comment

      • Will Ware

        #4
        Re: Restrictive APIs for Python

        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?

        Comment

        • Gabriel Genellina

          #5
          Re: Restrictive APIs for Python

          On 15 dic, 13:46, "Will Ware" <will.w...@gmai l.comwrote:
          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

          Comment

          Working...