associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "PrivaiMe"

Diferenças entre as versões de 1 e 2
Revisão 1e 2008-11-11 13:42:48
Tamanho: 8643
Editor: PedroWerneck
Comentário:
Revisão 2e 2008-11-11 13:43:50
Tamanho: 8640
Editor: PedroWerneck
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
Linha 137: Linha 137:
# Now, some test code # Um testezinho...

Você, que acha que toda linguagem OO tem de ter variáveis privadas porque aprendeu com uma que tem, acho que isso é um mandamento sagrado e acha que Python é uma linguagem pecadora porque não tem, suas orações foram atendidas. Usando o código abaixo você estará privado de todo o mal dos atributos públicos, exceto para alguém que esteja REALMENTE disposto a alterá-los, e esse merece ir para o inferno, não? Não? Não acha? Bom... então é melhor deixar isso pra lá porque talvez você acabe levando isso a sério.

Basta salvar o código abaixo em um módulo, importar a função 'private' para seu módulo e definir a variável 'metaclass = private('var1', 'var2', 'var3', ...) no início, listando todos os atributos que deseja deixar privados.

OBS: aos desavisados, esse código, nos moldes do NoSelf, também é uma brincadeira, feita para mostrar como atributos privados não são uma prioridade e que em Python, se você estiver realmente disposto, é possível restringir o acesso cada vez mais e mais (e aqui nem foi muito longe), mas se o usuário quer mesmo alterar, por que impedir?

Crédito ao João S. O. Bueno pela idéia original e algumas dicas. Ele também encontrou algumas maneiras de quebrar (que não mencionarei aqui), e para cada uma delas até encontrei uma maneira de proteger, mas acho que isso já basta para ilustrar a idéia...

   1 #!/usr/bin/env python
   2 # encoding: utf-8
   3 
   4 import inspect
   5 import sys
   6 import types
   7 import unittest
   8 
   9 
  10 class PrivateAttributeError(AttributeError):
  11     pass
  12 
  13 
  14 def _check_caller(obj, level):
  15     frame = sys._getframe(level)
  16     cls = type(obj)
  17 
  18     values = frame.f_locals.values()
  19     if obj not in values:
  20         return False
  21 
  22     funcs = [func for func in cls.__dict__.values() if isinstance(func, types.FunctionType)]
  23 
  24     for func in funcs:
  25         code1 = func.func_code
  26         code2 = frame.f_code
  27         if code1 is code2:
  28             break
  29     else:
  30         return False
  31     return True
  32 
  33 
  34 class Private(object):
  35     pass
  36 
  37 
  38 class PrivateAttribute(Private):
  39     def __init__(self, name):
  40         self.name = name
  41 
  42     def __get__(self, obj, cls=None):
  43         if not _check_caller(obj, 2):
  44             raise PrivateAttributeError("can only be used inside a method")
  45         try:
  46             value = obj.__privdict__[self.name]
  47         except KeyError:
  48             raise AttributeError("Private attribute '%s' not set"%self.name)
  49         return value
  50 
  51     def __set__(self, obj, value):
  52         if not _check_caller(obj, 2):
  53             raise PrivateAttributeError("can only be used inside a method")
  54         obj.__privdict__[self.name] = value
  55 
  56     def __delete__(self, obj):
  57         if not _check_caller(obj, 2):
  58             raise PrivateAttributeError("can only be used inside a method")
  59         try:
  60             del obj.__privdict__[self.name] 
  61         except KeyError:
  62             raise AttributeError("Private attribute '%s' not set"%self.name)
  63 
  64 
  65 class PrivateMethod(Private):
  66     def __init__(self, func):
  67         self.func = func
  68 
  69     def __get__(self, obj, cls=None):
  70         if not _check_caller(obj, 2):
  71             raise PrivateAttributeError("can only be used inside a method")
  72         return types.MethodType(self.func, obj, cls)
  73 
  74     def __set__(self, obj, value):
  75         raise RuntimeError("You can't reasign a private method")
  76 
  77     def __delete__(self, obj):
  78         raise RuntimeError("You can't reasign a private method")
  79     
  80     
  81 class PrivateDict(object):
  82     __slots__ = ['owner', '__privdict__']
  83     def __init__(self, owner):
  84         self.owner = owner
  85         self.__privdict__ = {}
  86         
  87     def __setitem__(self, key, value):
  88         if not _check_caller(self.owner, 3):
  89             raise PrivateAttributeError("Are you trying to break this?")
  90         self.__privdict__[key] = value
  91 
  92     def __getitem__(self, key):
  93         if not _check_caller(self.owner, 3):
  94             raise PrivateAttributeError("Are you trying to break this?")
  95         return self.__privdict__[key]
  96 
  97     def __getattribute__(self, attr):
  98         if not _check_caller(self, 2):
  99             raise PrivateAttributeError("Are you trying to break this?")
 100         return super(PrivateDict, self).__getattribute__(attr)
 101 
 102     def __setattribute__(self, attr):
 103         if not _check_caller(self, 2):
 104             raise PrivateAttributeError("Are you trying to break this?")
 105         return super(PrivateDict, self).__getattribute__(attr)
 106     
 107 
 108 class MetaEnablePrivate(type):
 109     def __call__(cls, *args, **kwds):
 110         new = cls.__new__(cls, *args, **kwds)
 111         new.__privdict__ = PrivateDict(new)
 112         cls.__init__(new, *args, **kwds)
 113         return new
 114 
 115 
 116 def private(*args):
 117     attrs = dict((name, PrivateAttribute(name)) for name in args)
 118 
 119     def _metaclass(name, bases, dict):
 120         dict.update(attrs)
 121         return MetaEnablePrivate(name, bases, dict)
 122 
 123     return _metaclass
 124 
 125 
 126 # Um testezinho...
 127 class Test(object):
 128     __metaclass__ = private('foo', 'bar')
 129     
 130     def __init__(self, name):
 131         self.foo = None
 132         self.bar = None
 133 
 134         self.name = name
 135         self.wow = None
 136 
 137     def set_foo(self, value):
 138         self.foo = value
 139         self.bar = "and I am new bar in %s"%self.name
 140 
 141     def get_foo(self):
 142         return self.foo
 143 
 144     @PrivateMethod
 145     def meth(self):
 146         return 'I am supposed to be a private method'
 147 
 148     def call_meth(self):
 149         return self.meth()
 150 
 151 
 152 class TestPrivateAttributes(unittest.TestCase):
 153     def setUp(self):
 154         self.obja = Test('a')
 155         self.objb = Test('b')
 156         self.obja.set_foo('I am foo in a')
 157         self.objb.set_foo('I am foo in b')
 158 
 159     def testGettersSetters(self):
 160         # get original
 161         self.assertEqual(self.obja.get_foo(), 'I am foo in a')
 162         self.assertEqual(self.objb.get_foo(), 'I am foo in b')
 163         # set
 164         self.obja.set_foo('I am new foo in a')
 165         self.objb.set_foo('I am new foo in b')
 166         # check if it really got set
 167         self.assertEqual(self.obja.get_foo(), 'I am new foo in a')
 168         self.assertEqual(self.objb.get_foo(), 'I am new foo in b')        
 169 
 170     def testBreakDirectGet(self):
 171         self.assertRaises(PrivateAttributeError, getattr, self.obja, 'foo')
 172         self.assertRaises(PrivateAttributeError, getattr, self.obja, 'bar')
 173         self.assertEqual(self.obja.wow, None)
 174         self.assertRaises(PrivateAttributeError, getattr, self.objb, 'foo')
 175         self.assertRaises(PrivateAttributeError, getattr, self.objb, 'bar')
 176         self.assertEqual(self.objb.wow, None)
 177 
 178     def testBreakDirectSet(self):
 179         self.assertRaises(PrivateAttributeError, setattr, self.obja, 'foo', '')
 180         self.assertRaises(PrivateAttributeError, setattr, self.obja, 'bar', '')
 181         self.obja.wow = ''
 182         self.assertRaises(PrivateAttributeError, setattr, self.objb, 'foo', '')
 183         self.assertRaises(PrivateAttributeError, setattr, self.objb, 'bar', '')
 184         self.objb.wow = ''
 185 
 186     def testWithObjectGetattribute(self):
 187         self.assertRaises(PrivateAttributeError, object.__getattribute__,
 188                           self.obja, 'foo')
 189         self.assertRaises(PrivateAttributeError, object.__getattribute__,
 190                           self.obja, 'bar')
 191         self.assertRaises(PrivateAttributeError, object.__getattribute__,
 192                           self.objb, 'foo')
 193         self.assertRaises(PrivateAttributeError, object.__getattribute__,
 194                           self.objb, 'bar')
 195 
 196     def testWithObjectSetattr(self):
 197         self.assertRaises(PrivateAttributeError, object.__setattr__,
 198                           self.obja, 'foo', '')
 199         self.assertRaises(PrivateAttributeError, object.__setattr__,
 200                           self.obja, 'bar', '')
 201         self.assertRaises(PrivateAttributeError, object.__setattr__,
 202                           self.objb, 'foo', '')
 203         self.assertRaises(PrivateAttributeError, object.__setattr__,
 204                           self.objb, 'bar', '')
 205 
 206     def testWithObjectPrivdict(self):
 207         self.assertRaises(PrivateAttributeError, lambda :self.obja.__privdict__['foo'])
 208         self.assertRaises(PrivateAttributeError, lambda :self.obja.__privdict__['bar'])
 209         self.assertRaises(PrivateAttributeError, lambda :self.objb.__privdict__['foo'])
 210         self.assertRaises(PrivateAttributeError, lambda :self.objb.__privdict__['bar'])
 211 
 212 
 213 
 214 if __name__ == '__main__':
 215     unittest.main()
 216