Preventing class methods from being defined

David Hirschfield davidh at ilm.com
Wed Jan 18 02:05:11 CET 2006


I realize now that it would probably be best to define the problem I'm 
having according to the constraints imposed by other requirements, and 
not by describing the basic functionality. That way, since there are so 
many possible ways to get something like what I want, there will 
probably be only one (or none) solutions to the problem that matches the 
criteria completely.

So, to restate the original problem: I want to be able to tag the 
methods of a class so that they only can be called if some external 
global says it's okay. Think of it as a security device for what can or 
cannot be called from a particular class instance. So if "restrict" is 
True, attempts to call methods on class A: foo() and bar(), will be 
disallowed, and raise an exception.

Now, here are the constraints:

Whatever implements this security should be something that class A is a 
subclass of, or is in some other way external to the class definition 
itself, since multiple classes will want to use this ability.

It can't be a metaclass, because for other reasons, class A already has 
a special metaclass, and you can't have two metaclasses on one class 
without having one metaclass inherit from the other.

You should not be able to "foil" the security by overriding the secured 
methods in a subclass of A. So if A.foo() is "secured" and you make 
class B a subclass of A and define b.foo(), b.foo() should also be secured.

The exception that gets raised when calling a restricted method when it 
isn't allowed should be able to indicate what method was called on what 
class.

I'd prefer not to override __getattribute__ to look at every attempt to 
access any attribute of the class instance to see if it's restricted.
It'd also be nice if the ability to restrict some method was done via a 
decorator-like syntax:

class X:
    def foo():
        ...
    restrict(foo)

So there's the problem and constraints. I've painted myself into a 
corner, and I realize that, but I have faith that someone out there who 
is far better at python than me, will know some way to do this, or 
something close to it.

Thanks again,
-David


Bengt Richter wrote:

>On Mon, 16 Jan 2006 18:55:43 -0800, David Hirschfield <davidh at ilm.com> wrote:
>
>  
>
>>Thanks for this, it's a great list of the ways it can be done. Here's a 
>>    
>>
>Actually, your way is yet another ;-)
>
>  
>
>>bit more insight into the arrangement I'm trying to get:
>>
>>restrict = True
>>    
>>
>Why a global value? If it is to affect class instantiation, why not pass it
>or a value to the constructor, e.g., C(True) or C(some_bool)?
>
>  
>
>>class A(object):
>>    
>>
>       ^--should that be R?
>  
>
>>   _restrict = ["test"]
>>  
>>   def _null(self, *args, **kws):
>>       raise Exception,"not allowed to access"
>>      
>>   def test(self):
>>       print "test restricted"
>>  
>>   def __init__(self):
>>       if restrict:
>>           for f in self._restrict:
>>               setattr(self,f,self._null)
>>    
>>
>I assume you know that you are using a bound method attribute
>on the instance to shadow the method of the class, for a per-instance
>effect as opposed to an all-instances shared effect.
>
>  
>
>>      
>>class C(R):
>>   def __init__(self):
>>       super(C,self).__init__()
>>      
>>   def test(self):
>>       print "test from c"
>>
>>
>>In this design, calling c.test() where c is an instance of C will raise 
>>an exception. Now, the only thing I'd like is to not have to fill out 
>>that _restrict list like that, but to have some function or something 
>>that let's me say which methods are restricted in the same way you 
>>define class methods or properties, i.e.:
>>
>>class A(object):
>>   _restrict = []
>>  
>>   def _null(self, *args, **kws):
>>       raise Exception,"not allowed to access"
>>      
>>   def test(self):
>>       print "test restricted"
>>   restrict(test)
>>   #### this does some magic to insert "test" into the _restrict list
>>
>>
>>I can't really find a way to make that work with descriptors, and it 
>>can't just be a function call, because I won't know what object to get 
>>the _restrict list from. Is there a way to refer to the class that "is 
>>being defined" when calling a function or classmethod?
>>So, ideas on how to accomplish that...again, greatly appreciated.
>>    
>>
>
>You can do it with a decorator, though it doesn't really do decoration,
>just adding the decoratee to the associated _restrict list. You don't
>have to factor out mkrdeco if you'r'e only defining the restrict decorator
>in one class.
>
>I changed A to R, and made the global restriction flag a constructor argument,
>but you can easily change that back, either by using the global restricted
>in R.__init__ as a global, or by passing it explicitly like c = C(restricted).
>
> >>> def mkrdeco(rlist):
> ...     def restrict(f):
> ...         rlist.append(f.func_name)
> ...         return f
> ...     return restrict
> ...
> >>> class R(object):
> ...     _restrict = []
> ...     restrict = mkrdeco(_restrict)
> ...     def _null(self, *args, **kws):
> ...         raise Exception,"not allowed to access"
> ...     def __init__(self, restricted):
> ...         if restricted:
> ...             for f in self._restrict:
> ...                 setattr(self,f,self._null)
> ...     @restrict
> ...     def test(self):
> ...         print "test restricted"
> ...
> >>> class C(R):
> ...    def __init__(self, restricted=False):
> ...        super(C,self).__init__(restricted)
> ...
> ...    def test(self):
> ...        print "test from c"
> ...
> >>> c = C(True)
> >>> c.test()
> Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
>   File "<stdin>", line 5, in _null
> Exception: not allowed to access
> >>> c2 = C(False)
> >>> c2.test()
> test from c
> >>> vars(c)
> {'test': <bound method C._null of <__main__.C object at 0x02EF3C4C>>}
> >>> vars(c2)
> {}
> >>> R._restrict
> ['test']
> 
>Still don't know what real application problem this is solving, but that's ok ;-)
>
>Regards,
>Bengt Richter
>  
>

-- 
Presenting:
mediocre nebula.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20060117/8c5635f2/attachment.html>


More information about the Python-list mailing list