Block setting of class attributes (__setattr__)

Question:

  1. is there a simple way to prevent setting new class attrs?
  2. while trying with the following snippet, shouldn’t setattr(Derived, "test1", 1) call the __setattr__ from Base?
class Base:
    def __setattr__(self, key, value):
        raise PermissionError('in base')
    
    def __init_subclass__(cls, *args, **kwargs):
        def _setattr_(inst, key, val):
            raise PermissionError('in derived')
        cls.__setattr__ = _setattr_
        
class Derived(Base):
    pass

setattr(Derived, "test1", 1)

Derived.__setattr__(Derived, "test2", 2)
Traceback (most recent call last):
.
.
.
PermissionError: in derived

Base.__setattr__(Derived, "test3", 3)
Traceback (most recent call last):
.
.
.
PermissionError: in base

EDIT:
this is a duplicate. See below.

Asked By: deponovo

||

Answers:

Not without a metaclass.

The __setattr__ method you define in a class only affects instances of that class (or instances of subclasses), but not subclasses or the class itself (this is true for most methods exlcuding stuff like __new__ and __init_subclass__).

setattr(cls, attr_name, value) calls cls.__class__.__setattr__(cls, attr_name, value), but Derived.__class__ == type, not Base

Answered By: MegaIng

After a better selection of keywords and another round of searches I found this problem solved in this SO answer. I am keeping it here as others might have the same difficulty to find one or the other, hence increasing the chances of getting a match.
Also here’s the solution to the prob (minimal working example):

class Meta(type):
    def __setattr__(self, key, value):
        raise PermissionError('in meta')

class Base(metaclass=Meta):
    def __setattr__(self, key, value):
        raise PermissionError('in base')

class Derived(Base):
    pass

setattr(Derived, "test1", 1)
.
.
.
PermissionError: in meta
Answered By: deponovo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.