Fully featured access modifier library for python
Project description
access-specifiers
This library provides runtime access modifiers with very high security and it is fully featured (e.g, supports private inheritance).
Installation
pip install access-specifiers
The recommended way to import the library is like below:
from access_specifiers import api as access_specifiers
This 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.
Inheritance
In order to make access modifiers available to your simple class, you need to inherit from Restricted class:
class MyClass(access_specifiers.Restricted):
pass
Metaclass 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)):
pass
Using The Modifiers
function 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):
pass
Alternatively, 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 = 30
The 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 = 30
The 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 = 10
Again, 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))):
pass
Utils
function 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):
pass
function access_specifiers.hook_descriptor(descriptor)
If you need to add a descriptor to a class after creating the class (not in the class body), you need to pass your descriptor object to this function and use its return value instead. It returns a DescriptorProxy object which itself is also a descriptor and wraps your descriptor. If you assign your own descriptor instead of a DescriptorProxy object, you will face PrivateError when trying to access the member implemented by the descriptor later on. The reasons for that is raw descriptors could be used to bypass private members. This function is automatically called for descriptors defined in the class body.
Restricted 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 value
method 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.
Limitations
- 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_objects
This isn't done by this library in case the user actually requires this function.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file access_specifiers-1.5.0.tar.gz.
File metadata
- Download URL: access_specifiers-1.5.0.tar.gz
- Upload date:
- Size: 44.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7da27c7899f305ab0ce3d01eb7c4a8c97fc7f23f452d140c2d14410c9d0402ad
|
|
| MD5 |
7866535aa8d1d649d64365d4a6a000a0
|
|
| BLAKE2b-256 |
9bc7ac67481bfd010dd6d1693cdd55db395f70eae3ea7f46f11526ec7fb60105
|
File details
Details for the file access_specifiers-1.5.0-py2.py3-none-any.whl.
File metadata
- Download URL: access_specifiers-1.5.0-py2.py3-none-any.whl
- Upload date:
- Size: 43.6 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
797415b0e515b69c2118c89201e89f6729753e6a156cb812493abbd88423ef72
|
|
| MD5 |
4bf6d3f6cbf036fb2d3f31ed0c334831
|
|
| BLAKE2b-256 |
b8e6fac6b5b9f71d8598cd066bc93c661c2bd7ec1b44696aba4c1e76087c16ba
|