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

Você não tem permissão para executar esta ação.

Excluir mensagem

PrivaiMe

Você, que acha que toda linguagem OO tem de ter variáveis privadas porque aprendeu com uma que tem?

Acha 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á e não continuar lendo 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...

#!/usr/bin/env python
# encoding: utf-8

import inspect
import sys
import types
import unittest


class PrivateAttributeError(AttributeError):
    pass


def _check_caller(obj, level):
    frame = sys._getframe(level)
    cls = type(obj)

    values = frame.f_locals.values()
    if obj not in values:
        return False

    funcs = [func for func in cls.__dict__.values() if isinstance(func, types.FunctionType)]

    for func in funcs:
        code1 = func.func_code
        code2 = frame.f_code
        if code1 is code2:
            break
    else:
        return False
    return True


class Private(object):
    pass


class PrivateAttribute(Private):
    def __init__(self, name):
        self.name = name

    def __get__(self, obj, cls=None):
        if not _check_caller(obj, 2):
            raise PrivateAttributeError("can only be used inside a method")
        try:
            value = obj.__privdict__[self.name]
        except KeyError:
            raise AttributeError("Private attribute '%s' not set" % self.name)
        return value

    def __set__(self, obj, value):
        if not _check_caller(obj, 2):
            raise PrivateAttributeError("can only be used inside a method")
        obj.__privdict__[self.name] = value

    def __delete__(self, obj):
        if not _check_caller(obj, 2):
            raise PrivateAttributeError("can only be used inside a method")
        try:
            del obj.__privdict__[self.name] 
        except KeyError:
            raise AttributeError("Private attribute '%s' not set" % self.name)


class PrivateMethod(Private):
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls=None):
        if not _check_caller(obj, 2):
            raise PrivateAttributeError("can only be used inside a method")
        return types.MethodType(self.func, obj, cls)

    def __set__(self, obj, value):
        raise RuntimeError("You can't reasign a private method")

    def __delete__(self, obj):
        raise RuntimeError("You can't reasign a private method")
    
    
class PrivateDict(object):
    __slots__ = ['owner', '__privdict__']
    def __init__(self, owner):
        self.owner = owner
        self.__privdict__ = {}
        
    def __setitem__(self, key, value):
        if not _check_caller(self.owner, 3):
            raise PrivateAttributeError("Are you trying to break this?")
        self.__privdict__[key] = value

    def __getitem__(self, key):
        if not _check_caller(self.owner, 3):
            raise PrivateAttributeError("Are you trying to break this?")
        return self.__privdict__[key]

    def __getattribute__(self, attr):
        if not _check_caller(self, 2):
            raise PrivateAttributeError("Are you trying to break this?")
        return super(PrivateDict, self).__getattribute__(attr)

    def __setattribute__(self, attr):
        if not _check_caller(self, 2):
            raise PrivateAttributeError("Are you trying to break this?")
        return super(PrivateDict, self).__getattribute__(attr)
    

class MetaEnablePrivate(type):
    def __call__(cls, *args, **kwds):
        new = cls.__new__(cls, *args, **kwds)
        new.__privdict__ = PrivateDict(new)
        cls.__init__(new, *args, **kwds)
        return new


def private(*args):
    attrs = dict((name, PrivateAttribute(name)) for name in args)

    def _metaclass(name, bases, dict):
        dict.update(attrs)
        return MetaEnablePrivate(name, bases, dict)

    return _metaclass


# Um testezinho...
class Test(object):
    __metaclass__ = private('foo', 'bar')
    
    def __init__(self, name):
        self.foo = None
        self.bar = None

        self.name = name
        self.wow = None

    def set_foo(self, value):
        self.foo = value
        self.bar = "and I am new bar in %s"%self.name

    def get_foo(self):
        return self.foo

    @PrivateMethod
    def meth(self):
        return 'I am supposed to be a private method'

    def call_meth(self):
        return self.meth()


class TestPrivateAttributes(unittest.TestCase):
    def setUp(self):
        self.obja = Test('a')
        self.objb = Test('b')
        self.obja.set_foo('I am foo in a')
        self.objb.set_foo('I am foo in b')

    def testGettersSetters(self):
        # get original
        self.assertEqual(self.obja.get_foo(), 'I am foo in a')
        self.assertEqual(self.objb.get_foo(), 'I am foo in b')
        # set
        self.obja.set_foo('I am new foo in a')
        self.objb.set_foo('I am new foo in b')
        # check if it really got set
        self.assertEqual(self.obja.get_foo(), 'I am new foo in a')
        self.assertEqual(self.objb.get_foo(), 'I am new foo in b')        

    def testBreakDirectGet(self):
        self.assertRaises(PrivateAttributeError, getattr, self.obja, 'foo')
        self.assertRaises(PrivateAttributeError, getattr, self.obja, 'bar')
        self.assertEqual(self.obja.wow, None)
        self.assertRaises(PrivateAttributeError, getattr, self.objb, 'foo')
        self.assertRaises(PrivateAttributeError, getattr, self.objb, 'bar')
        self.assertEqual(self.objb.wow, None)

    def testBreakDirectSet(self):
        self.assertRaises(PrivateAttributeError, setattr, self.obja, 'foo', '')
        self.assertRaises(PrivateAttributeError, setattr, self.obja, 'bar', '')
        self.obja.wow = ''
        self.assertRaises(PrivateAttributeError, setattr, self.objb, 'foo', '')
        self.assertRaises(PrivateAttributeError, setattr, self.objb, 'bar', '')
        self.objb.wow = ''

    def testWithObjectGetattribute(self):
        self.assertRaises(PrivateAttributeError, object.__getattribute__,
                          self.obja, 'foo')
        self.assertRaises(PrivateAttributeError, object.__getattribute__,
                          self.obja, 'bar')
        self.assertRaises(PrivateAttributeError, object.__getattribute__,
                          self.objb, 'foo')
        self.assertRaises(PrivateAttributeError, object.__getattribute__,
                          self.objb, 'bar')

    def testWithObjectSetattr(self):
        self.assertRaises(PrivateAttributeError, object.__setattr__,
                          self.obja, 'foo', '')
        self.assertRaises(PrivateAttributeError, object.__setattr__,
                          self.obja, 'bar', '')
        self.assertRaises(PrivateAttributeError, object.__setattr__,
                          self.objb, 'foo', '')
        self.assertRaises(PrivateAttributeError, object.__setattr__,
                          self.objb, 'bar', '')

    def testWithObjectPrivdict(self):
        self.assertRaises(PrivateAttributeError, lambda :self.obja.__privdict__['foo'])
        self.assertRaises(PrivateAttributeError, lambda :self.obja.__privdict__['bar'])
        self.assertRaises(PrivateAttributeError, lambda :self.objb.__privdict__['foo'])
        self.assertRaises(PrivateAttributeError, lambda :self.objb.__privdict__['bar'])



if __name__ == '__main__':
    unittest.main()