Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Protect class attributes in any python object instance

License

NotificationsYou must be signed in to change notification settings

sundarnagarajan/pyprotect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pyprotect is a python module that provides API to restrict visibility or mutability of selected Python object attributes in a robust manner.

The key functions in the pyprotect module API -private() andprotect() wrap the python object (like aProxy) to restrict visibility or mutability of selected attributes of the wrapped object, while allowing thewrapping object to behave virtually identical to thewrapped object.

Features

  • Can wrap virtually any Python object - instances, classes (types), modules, methods, classmethods, instancemethods, staticmethods, partials, lambdas.
  • Tested on Python 2.7 and Python 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11
  • Tested on the following distributions with the latest versions of python2, python3, pypy3, pypy shipped by the respective distributions:
    • Ubuntu Jammy 22.04
    • Arch linux (20220101)
    • Fedora 37
    • Alpine Linux 3.15 (Alpine 3.16 does not have python2-dev)
  • Has extensive unit (functional) tests - intests directory.

Table of Contents

Quick start

freeze(o:object)->Frozen:
  • Ifo is immutable (e.g. int , string), returnso UNCHANGED
  • Ifo is Wrapped, returnso UNCHANGED if object WRAPPPED INSIDEo is immutable, returns Frozen otherwise
  • Ifo is Frozen, returnso UNCHANGED
  • Ifo is FrozenPrivate, FrozenProtected or FrozenPrivacyDict, returnso UNCHANGED
  • Ifo is Private, returns FrozenPrivate
  • Ifo is Protected, returns FrozenProtected
  • Otherwise, returns Frozen

Object returned prevents modification of ANY attribute

private(o:object,frozen:bool=False)->object:
  • Iffrozen is False:
    • Ifo is an instance of Private, returnso UNCHANGED
    • If 'o' is an instance of Protected, returnso UNCHANGED
  • Iffrozen is True:
    • Ifo is an instance of Private, returnsfreeze(o) (FrozenPrivate)
    • Ifo is an instance of Protected, returnsfreeze(o) (FrozenProtected)
    • Otherwise:Iffrozen is True, returns FrozenPrivate; returns Private otherwise
protect(o:objectfrozen:bool=False,dynamic:bool=True,hide_private:bool=False,ro_data:bool=False,ro_method:bool=True,ro:List[str]= [],rw:List[str]= [],hide:List[str]= [])->object:# o-->object to be wrapped

Returns-->Instance ofFrozenProtected iffrozen; Instance ofProtected otherwise

Ifprotect() is called on an object 'o' that is an instance of Protected,protect() will merge theprotect() rules, enforcing the most restrictive combination among the two sets of protect() options:

  • hide andhide_private are OR-ed
  • ro_method,ro_data andro are OR-ed
  • rw is AND-ed, butrw of second protect overridesro* ofsecond protect butnot thefirst protect.

In short, by calling protect() a second time (or multiple times):

  • Additoinal attributes can be hidden
  • Additional attributes can be made read-only
  • No previously hidden attribute will become visible
  • No previously read-only attribute will become mutable

Options: protect method arguments

OptionTypeDefaultDescriptionOverrides
frozenboolFalseIf True, no attributes can be changed, added or deleted
hide_privateboolFalseIf True, private vars of the form_var will be hidden
ro_databoolFalseData (non-method) attributes will be immutable
Can override selectively withrw
ro_methodboolTrueMethod (callable) attributes will be immutable
Can override selectively withrw
rolist of str[ ]Attributes that will be immutable
Can override selectively withrw
rwlist of str[ ]Attributes that will be mutablero_data
ro_method
ro
hidelist of str[ ]

Visibility and mutability of attributes with protect() method

OptionAttribute TypeRestricts VisibilityRestricts Mutability
frozenAnyNOYES
hide_privatePrivate attributesYESYES (Indirect)
ro_dataData attributesNOYES
ro_methodMethod attributesNOYES
roANYNOYES
rwANYNOYES
hideANYYESYES (Indirect)

Classes

class diagram

Features of key classes

Wrapped

  • Visibility: No additional restrictions
  • Mutability: No additional restrictions

Frozen

  • Visibility: Does notadditionally restrict visibility of any attributes inwrapped object accessed throughwrapping object
  • Mutability: Prevents modification of ANY attribute

Private

  • Visibility:
    • Cannot access traditionally 'private' mangled python attributes
    • Cannot modify traditionally private attributes (form 'var')
    • Attributes not part of dir(wrapped_object) are not visible
  • Mutability:
    • The following attributes of wrapped object are NEVER writeable:__class__,__dict__,__delattr__,__setattr__,__slots__,__getattribute__
    • Traditional (mangled) Python private vars are ALWAYS hidden
    • Private vars (form _var) will be read-only
    • Attributes cannot be added or removed
    • Attributes that are properties are ALWAYS visible AND WRITABLE (except if 'frozen' is used)
      • Properties indicate an intention of class author to expose them
      • Whether they are actually writable depends on whether class author implemented property.setter

FrozenPrivate

  • Visibility: Same as Private
  • Mutability: Prevents modification of ANY attribute

Protected

  • Features of Private PLUS allowsfurther restriction of:
    • Which attributes are VISIBLE
    • Which attributes are WRITEABLE
  • Default settings:
    • Features of Private - see above
    • dynamic == TrueAttribute additions, deletions, type changes automatically visible
    • ro_method == True: Method attributes will be read-only
    • All other non-private data attributes are read-write

FrozenProtected

  • Features of Protected PLUS prevents modification of ANY attribute

API

Wrapping API

freeze

freeze(o:object)->Frozen:
  • Ifo is immutable (e.g. int , string), returnso UNCHANGED
  • Ifo is Wrapped, returnso UNCHANGED if object WRAPPPED INSIDEo is immutable, returns Frozen otherwise
  • Ifo is Frozen, returnso UNCHANGED
  • Ifo is FrozenPrivate, FrozenProtected or FrozenPrivacyDict, returnso UNCHANGED
  • Ifo is Private, returns FrozenPrivate
  • Ifo is Protected, returns FrozenProtected
  • Otherwise, returns Frozen

Object returned prevents modification of ANY attribute

private

private(o:object,frozen:bool=False)->object:
  • Iffrozen is False:
    • Ifo is an instance of Private, returnso UNCHANGED
    • If 'o' is an instance of Protected, returnso UNCHANGED
  • Iffrozen is True:
    • Ifo is an instance of Private, returnsfreeze(o) (FrozenPrivate)
    • Ifo is an instance of Protected, returnsfreeze(o) (FrozenProtected)
    • Otherwise:Iffrozen is True, returns FrozenPrivate; returns Private otherwise

protect

protect(o:objectfrozen:bool=False,dynamic:bool=True,hide_private:bool=False,ro_data:bool=False,ro_method:bool=True,ro:List[str]= [],rw:List[str]= [],hide:List[str]= [])->object:# o-->object to be wrapped

Returns-->Instance ofFrozenProtected iffrozen; Instance ofProtected otherwise

Ifprotect() is called on an object 'o' that is an instance of Protected,protect() will merge theprotect() rules, enforcing the most restrictive combination among the two sets of protect() options:

  • hide andhide_private are OR-ed
  • ro_method,ro_data andro are OR-ed
  • rw is AND-ed, butrw of second protect overridesro* ofsecond protect butnot thefirst protect.

In short, by calling protect() a second time (or multiple times):

  • Additoinal attributes can be hidden
  • Additional attributes can be made read-only
  • No previously hidden attribute will become visible
  • No previously read-only attribute will become mutable

Options: protect method arguments

OptionTypeDefaultDescriptionOverrides
frozenboolFalseIf True, no attributes can be changed, added or deleted
hide_privateboolFalseIf True, private vars of the form_var will be hidden
ro_databoolFalseData (non-method) attributes will be immutable
Can override selectively withrw
ro_methodboolTrueMethod (callable) attributes will be immutable
Can override selectively withrw
rolist of str[ ]Attributes that will be immutable
Can override selectively withrw
rwlist of str[ ]Attributes that will be mutablero_data
ro_method
ro
hidelist of str[ ]

Visibility and mutability of attributes with protect() method

OptionAttribute TypeRestricts VisibilityRestricts Mutability
frozenAnyNOYES
hide_privatePrivate attributesYESYES (Indirect)
ro_dataData attributesNOYES
ro_methodMethod attributesNOYES
roANYNOYES
rwANYNOYES
hideANYYESYES (Indirect)

wrap

wrap(o:object)->Wrapped:
  • Should behave just like the wrapped object, except following attributes cannot be modified:__getattribute__,__delattr__,__setattr__,__slots__
  • Explicitly does NOT support pickling, and will raisepickle.PicklingError
  • Does NOT protect CLASS (or__class__) of wrapped object from modification
  • Does NOT protect__dict__ or__slots__

Useful for testing if wrapping is failing for a particular type of object

Checking types of wrapped objects

isfrozen

isfrozen(x:object)->bool

x was created usingfreeze() orprivate(o, frozen=True) orprotect(o, frozen=True)

isimmutable

isimmutable(x:object)->bool

x is known to be immutable

isprivate

isprivate(x:object)->bool

x was created usingprivate()

isprotected

isprotected(x:object)->bool

x was created usingprotect()

Checking properties of objects inside wrapped objects

contains

contains(w:object,o:object)->bool

Ifw is a wrapped object (iswrapped(w) is True), returns whetherw wrapsoOtherwise unconditionally returns False

help_protected

help_protected(x:object)->None

Ifx wrapso, executeshelp(o)Otherwise executes h_elp(x)_

id_protected

id_protected(x:object)->int

ifx is a wrapped object (iswrapped(x) is True) andx wrapso, returnsid(o)Otherwise returnsid(x)

isinstance_protected

isinstance_protected(x:object,t:type)->bool

Ifx is a wrapped object (iswrapped(x) is True) andx wrapso, returnsisinstance(o, t)Otherwise returnsisinstance(x, t)

isreadonly

isreadonly(x:object,a:str)->bool

Ifx is a wrapped object - withiswrapped(x) == True - andx wrapso,isreadonly(x, a) returns whether rules ofwrapper make attributea read-only when accessed throughxThis representsrule of wrapped object - does not guarantee that_o_ has attribute_a_ or that setting attributea in objecto will not raise any exceptionIfx isnot a wrapped object (iswrapped(x) is False) , unconditionally returns False

instance_of_protected

instance_of_protected(x:object,o:object)->bool

Ifx is a wrapped object - withiswrapped(x) == True - andx wrapso,instance_of_protected(x, o) returns True if and only ifisinstance(x, type(o))Ifx is not a wrapped object -iswrapped(x) == False -instance_of_protected(x, o) returnsisinstance(x, o)

isvisible

isvisible(x:object,a:str)->bool

Returns False if and only ifiswrapped(x) is True ANDx makes attributea invisible if present in wrapped object
This representsrule of wrapped object - does not guarantee thatwrapped object has attributea or that accessing attributea in objectx will not raise any exception

Ifx is not a wrapped object, unconditionally returns False

same_class_protected

same_class_protected(c:type,w:object)->bool

Ifiswrapped(w) andw wrapso: Returns(c is type(o))
Otherwise: returns(c is type(w))

subclass_of_protected

subclass_of_protected(x:object,w:object)->bool

Ifiswrapped(w) andw wrapso: Returnsissubclass(x, type(o))
Otherwise: returnsissubclass(x, w)

pyprotect module metadata

immutable_builtin_attributes

immutable_builtin_attributes()->Set[str]

Returns-->set of str: attributes in builtins that are immutableUsed in unit tests

always_delegated_attributes

always_delegated_attributes()->set(str)

Attributes that are always delegated to wrapped object

attribute_protected

attribute_protected()->str

Name of special attribute in Wrapped objects

hidden_pickle_attributes

hidden_pickle_attributes()->set(str)

Attributes that are never visible in object 'o' if iswrapped(o) - to disallow pickling

never_writeable

never_writeable()->set(str)

Attributes that are never writeable in objecto ifiswrapped(o)

never_writeable_private

never_writeable_private()->set(str)

Attributes that are never writeable in objecto ifisprivate(o)

Calling wrap operations multiple times

In the table below:

  • The left-most column shows starting state.
  • The top row shows operation applied to the starting state.
  • The intersecting cell shows the result.
  • UNCH represents operation returning the starting state unchanged
Operation 🡆
On type 🡇
wrapfreezeprivateprivate
+ frozen
protectprotect
+ frozen
Ordinary object
iswrapped(x) is False
WrappedFrozen
(2)
PrivateFrozenPrivateProtectedFrozenProtected
WrappedUNCHFrozen
(2)
PrivateFrozenPrivateProtectedFrozenProtected
FrozenWrapped
(2)
UNCH
(2)
FrozenPrivateFrozenPrivateFrozenProtectedFrozenProtected
PrivateUNCHFrozenPrivateUNCHFrozenPrivateProtectedFrozenProtected
FrozenPrivateUNCHUNCHUNCHUNCHFrozenProtectedFrozenProtected
ProtectedUNCHFrozenProtectedUNCHFrozenProtectedProtected
(1)
FrozenProtected
(1)
FrozenProtectedUNCHUNCHUNCHUNCHFrozenProtected
(1)
FrozenProtected
(1)
  1. protect() applied twice will merge theprotect() rules, enforcing the most restrictive combination among the two sets of protect() options:
  • hide andhide_private are OR-ed
  • ro_method,ro_data andro are OR-ed
  • rw is AND-ed, butrw of second protect overridesro* ofsecond protect butnot thefirst protect.

In short, by calling protect() a second time (or multiple times):- Additoinal attributes can be hidden- Additional attributes can be made read-onlybut:- No previously hidden attribute will become visible- No previously read-only attribute will become mutable

  1. Ifx is an immutable object (e.g. int, str ...) havingisimmutable(x) == True,freeze(x) returnsx andiswrapped(freeze(x)) will be False.

For all other objectsx, havingisimmutable(x) == False,freeze(x) will return a Frozen object havingiswrapped(freeze(x)) == True

For all other wrapped objectsw, created withprivate(x) orprotect(x),freeze(w) will always return a Wrapped object withiswrapped(w) == True

Python rules for attributes of type 'property':

  • Properties are defined in the CLASS, and cannot be changed in the object INSTANCE
  • Properties cannot be DELETED
  • Properties cannot be WRITTEN to unless property has a 'setter' method defined in the CLASS
  • These rules are implemented by the python language (interpreter) and Protected class does not enforce or check

What kind of python objects can be wrapped?

Pretty much anything. pyprotect only mediates attribute access usingobject.__getattribute__,object.__setattr__ andobject.__delatr__. If these methods work on your object, your object can be wrapped

Work in progress

Changelog

Jan-20-2022

  • Project name on github changed topyprotect to match the name of the module. This has been long-pending. Theold github link redirects to the new project name.

Jan-15-2022

  • Started signing commits with my GPG key and displaying 'verified' for signed commits

Dec-08-2022

A number of parameters to protect() have been discontinued. See list and reasons below, as well as how to achieve the same effect without thos parameters (sometimes, it takes more work). Most of them would be realistically useful very rarely, and / or do not align with what I call 'idiomatic python'.

hide_all, hide_method, hide_dunderSo-called 'special methods' in Python serve an important functional roles - especially in emulating containers (tuples, lists, sets, dicts), emulating numeric types supporting arithmetic operators, numeric comparisons etc. If such specific 'dunder methods' were hidden, it would definitely affect the behavior of the wrapped object.hide_dunder would hide all such special methods.hide_method would in addition hide all methods in the objest.hide_all would hide all object attributes, making the object virtually useless, and the option useful in testing (if at all).

hide_dataIn most cases, hiding all non-method data attributes will make the object less useful / cripple the expected usage of the object. Specific use-cases can be achieved using the 'hide' parameter.

ro_dunderSeems like it can be replaced by using ro_method' Mostly, in 'idiomatic python', methods of a class / instance are not mutated from outside the class / instance. This expected 'idiomatic python' behavior can be achieved with 'ro_method'.

ro_allIs unnecessary, since 'frozen' can be used instead.

addIn my opinion, 'add' does not align with 'idiomatic python'. While Python allows users of a class / instance adding attributes to the class / instance, that is not the expected style. Based on this, I have decided to promote that 'idiomatic style', and prevent adding / deleting any attributes in a Private or Protected object.

showIt is unnecessary, since all attributes are visible by default in Python. Only 'hide' or 'hide_private' will hide any attributes.

This leaves the following (incidentally also reducing testing load):

- Visibility:- hide_private- hide- Mutability:- ro_data- ro_method- ro- rw- frozen- Behavior:- dynamic

[8]ページ先頭

©2009-2025 Movatter.jp