This library provides runtime access modifiers with very high security and it is fully featured (e.g, supports private inheritance).
pip install access-specifiers
The recommended way to import the library is like below:
from access_specifiers import api as access_specifiersThis convoluted import statement is required in order to have a strong access security. Shortly, api protects the library from monkeypatching. Rest of the documentation assumes the library is imported as shown above.
In order to make access modifiers available to your simple class, you need to inherit from Restricted class:
class MyClass(access_specifiers.Restricted):
passMetaclass of Restricted is Restrictor. If you need to inherit from classes which inherit from Restricted, you first need to create a new metaclass.
access_specifiers.create_restrictor(*bases)
Create a metaclass given the required bases.
class MyClass4(metaclass = access_specifiers.create_restrictor(MyClass1, MyClass2, MyClass3)):
passfunction access_specifiers.private(value)
decorator @access_specifiers.private(value)
function access_specifiers.protected(value)
decorator @access_specifiers.protected(value)
function access_specifiers.public(value)
decorator @access_specifiers.public(value)
Modifiers can be used both as a function and a decorator. Just call them with the value you need to set its modifier:
class MyClass(access_specifiers.Restricted):
a = access_specifiers.private(10)
@access_specifiers.private
def func(self):
passAlternatively, you can also use a fancier syntax:
class access_specifiers.PrivateModifier
class access_specifiers.ProtectedModifier
class access_specifiers.PublicModifier
private = access_specifiers.PrivateModifier
protected = access_specifiers.ProtectedModifier
public = access_specifiers.PublicModifier
class MyClass(access_specifiers.Restricted):
private .a = 10
protected .b = 20
public .c = 30The dot (.) in between the modifier and the name is required.
You can also set new members with access modifiers after creating the class. Classes inheriting Restricted store ClassModifier objects. You can use them as modifiers:
class MyClass(access_specifiers.Restricted):
@classmethod
def func(cls):
private = cls.private
protected = cls.protected
public = cls.public
private .d = 10
protected .e = 20
public .f = 30The dot (.) in between the modifier and the name is required.
You can also specify access modifiers for object attributes. Restricted objects store Modifier objects. You can use them as modifiers:
class MyClass(access_specifiers.Restricted):
def func(self):
private = self.private
protected = self.protected
public = self.public
private .a = 10Again, the dot in between is required.
function access_specifiers.set_default(modifier)
Set the default modifier when a member is defined with no explicit modifier. By default, default modifier is public. modifier parameter can be either access_specifiers.private, access_specifiers.protected or access_specifiers.public.
There is one more feature of access_specifiers.create_restrictor: private and protected inheritance. Replace your base classes with calls to modifiers:
class MyClass2(metaclass = access_specifiers.create_restrictor(access_specifiers.private(MyClass))):
pass
class MyClass3(metaclass = access_specifiers.create_restrictor(access_specifiers.protected(MyClass))):
passfunction access_specifiers.super(obj_or_cls = None)
This function is equivalent to the built in super function and it should be used when the built in function doesn't work. Returns a proxy object to the superclass of obj_or_cls.
decorator @access_specifiers.Decorator(decorator)
Normally, if you decorate a method, the function returned by the decorator becomes the member and the original function won't be a member. This causes the original function to be unable to access private/protected members. Instead of decorating directly, pass your decorator to access_specifiers.Decorator() and this problem will be solved. Along with the original function, all the wrapper functions returned by each of the decorators will also be authorized. Lastly, access decorators must be topmost and you shouldn't pass them in access_specifiers.Decorator(). Example usage:
def factory1(func):
def wrapper(*args, **kwargs):
print("by factory1")
func(*args, **kwargs)
return wrapper
def factory2(func):
def wrapper(*args, **kwargs):
print("by factory2")
func(*args, **kwargs)
return wrapper
class MyClass(access_specifiers.Restricted):
@access_specifiers.private
@access_specifiers.Decorator(factory1)
@access_specifiers.Decorator(factory2)
def func(self):
passRestricted class provides a few more useful things:
method Restricted.set_private(name, value, cls = None)
method Restricted.set_protected(name, value, cls = None)
method Restricted.set_public(name, value)
You can specify modifiers for dynamically generated variable names. If cls is specified it must be a class and the call to these functions will be treated as if it is done from one of the methods inside the cls. cls can either be the same as of the caller or it must be more derived than of the caller. If it is a parent of the caller's class, an access_specifiers.PrivateError will be raised.
method Restricted.authorize(func_or_cls, for_all = True)
This function acts like the "friend" keyword of c++. Allows func_or_cls to have as much member access right as any other method of this class. func_or_cls can either be a function or a class. If for_all is set, allows func_or_cls to access private/protected members of not only this object, but also every other instantiated object of this class and also all future objects of this class which will be instantiated later and even the class itself.
method Restricted.get_hidden_value(value, name = None)
Return a protected object whose only attribute "value" stores the given value. Access to the value attribute is only granted if the accessing function has the rights to access given name. If name is None, any class in the class hierarchy of this object can access the value but external access is rejected. If you are calling this method from a base class called MyBase and don't want any derived class to access value, name must be one of the private members of MyBase. Each object whose class derives from Restricted has private members whose names has the following structure: class_name + "_" + "private". For the case of MyBase, you can set name to "MyBase_private". This function is useful in case you wanna call an external function and want to prevent that function from obtaining (possibly private) local variables of the calling method. Example usage:
import sys
def external_func():
print(sys._getframe(1).f_locals)
class Base(access_specifiers.Restricted):
pass
class Derived(metaclass = access_specifiers.create_restrictor(Base)):
def __init__(self):
private = self.private
private .a = 10
def func(self):
a = self.a
hidden_value = self.get_hidden_value(a, name = "Derived_private")
del a
external_func()
a = hidden_value.value
obj = Derived()
obj.func()method Restricted.create_getattribute()
Return a __getattribute__ function which checks the access rights of the caller. Useful when you write a custom __getattribute__ and don't wanna manually check the caller:
def __getattribute__(self, name):
getter = self.create_getattribute()
value = getter(name)
return valuemethod Restricted.create_setattr()
Return a __setattr__ function which checks the access rights of the caller. Useful when you write a custom __setattr__ and don't wanna manually check the caller:
def __setattr__(self, name, value):
setter = self.create_setattr()
setter(name, value)method Restricted.create_delattr()
Return a __delattr__ function which checks the access rights of the caller. Useful when you write a custom __delattr__ and don't wanna manually check the caller:
def __delattr__(self, name):
deleter = self.create_delattr()
deleter(name)Restricted._subclasses_
This is a class variable holding a list of subclasses. Elements of this list doesn't check access to their private and protected members but do check to private members coming from their bases.
Functions below are provided by Restrictor, which means they are only available to classes, not objects:
method Restrictor.set_class_private(name, value)
method Restrictor.set_class_protected(name, value)
method Restrictor.set_class_public(name, value)
After the class has been created, these methods can be used to set private, protected and public class members which have dynamically generated names.
method Restrictor.authorize_for_class(func_or_cls)
Authorize func_or_cls so it can access private and protected class members. func_or_cls can either be a function or a class. func_or_cls will also be authorized to access private and protected members of future objects, but not current ones.
- gc.get_objects() can leak private/protected members. In order to prevent this, you may consider adding this to the top of your code:
import gc
del gc.get_objectsThis isn't done by this library in case the user actually requires this function.
- @staticmethod is not supported