warvariuc
warvariuc

Reputation: 59624

A module's __setattr__ and __getattr__ when accessing globals

__getattr__ and __setattr__ works well when i get or set an object's attributes using dot notation. How do i intercept getting and setting module global variables from inside the module?

I subclass module type:

from types import ModuleType
class WUserModule(ModuleType):
    def __init__(self, name):
        super().__init__(name)

    def __setattr__(self, name, value):
        if name in self.__dict__:
             super().__setattr__(name, value)
             return
        print('Set name {}, value {}'.format(name, str(value)))

    def __getattr__(self, name):
        print('Get name {}'.format(name))

Create an empty module and load code into it:

module = WUserModule('form_module')
with open('user_module.py', 'r', encoding='utf8') as f:
    exec(f.read(), module.__dict__)

The code loaded from user_module.py:

a=1
print(a)

I need somehow to intercept access to variable a. In loaded code there is expected access to some variables which do not exist in module globals() and i want to substitute requested values.

UPDATE:

The code above doesn't work as i need: access to variable a is not reflected by print.

I am using PyQt4 to write a 'platform', where other users (programmers) add forms and modules which handle interaction with these forms. The form itself is accessible in user module via 'injected' variable form. I want to give users possibility to access values from form widgets and a simpler way. Instead of writing if form.myCheckbox.isChecked() or form.myCheckbox.setChecked(myValue) i want to provide the a shortcut: if myCheckbox and myCheckbox = myValue, intercepting values access and making needed work in the background:

def __getattr__(self, name):
    formWidget = getatttr(form, name)
    if isinstance(formWidget, QLineEdit):
        formWidget.setText(value)
...

Upvotes: 3

Views: 1578

Answers (3)

Sergey Fiantsev
Sergey Fiantsev

Reputation: 11

this works:

assignments = {}

# extending dict class
class WUserModule(dict):
    def __init__(self, dict):
        self.__dict__ = globals() | dict # to support of import statements inside exec
    def __setitem__(self, key, value):
        self.__dict__[key] = value
        print(f"new assinment {key} = {value}")
        assignments[key] = value
    def __getitem__(self, key): # without this method doesn't work - don't know why ;)
        return self.__dict__[key]

# initial values if needed
input_vars_dict={"a":0}
module = WUserModule(input_vars_dict)
with open('user_module.py', 'r', encoding='utf8') as f:
    exec(f.read(), module)

# assignments filled with all assignment that happpened
print(assignments)

userland module:

print("user_module.py")
print(a)
a=1
b=2

output:

user_module.py
0
new assinment a = 1
new assinment b = 2
{'a': 1, 'b': 2}

Upvotes: 0

SingleNegationElimination
SingleNegationElimination

Reputation: 156198

You can't.

The basic problem is that attribute overloads only work when you are accessing an attribute, specifically that means:

expr . ident

Without the dot, there can be no attribute overload. Thus, no matter what you do, a sequence like

a

Can never invoke an attribute overload, no matter what else may equate to the value of a.

Upvotes: 2

Rosh Oxymoron
Rosh Oxymoron

Reputation: 21055

What you're trying to do can't be done in the general case. You can't have an attribute both have a value and an attribute. You can either do form.myCheckbox = True and bool(form.myCheckbox), or form.myCheckbox.setChecked(True) and bool(form.myCheckbox.isChecked()), not both. I mean, form.myCheckbox can't be True and a QCheckbox object at the same time. You could make it work for bool, int and unicode can be handled but inconveniently, while other types are generally impossible.

Other than that, you're confused about what __getattr__ does. It's not called for attributes that exist, __getattribute__ is. But using __getattribute__ is complicated, you might want to make your checkboxes descriptors or use properties, but then you'd have to give up on modules and use classes.

P.S. Why is form a module, and not an instance of some object?

Upvotes: 0

Related Questions