Zelphir Kaltstahl
Zelphir Kaltstahl

Reputation: 6189

How to write a decorator with same import as class in which it is used?

How do I write a decorator in Python, which has some import from Foo import Foo, which is needed, when I want to use it for methods of a class, which also has this import?

Putting the import into both separate files will cause an error, telling me that Foo cannot be imported from Foo.

If it's not possible then this: Is it a good idea, if the decorator is not going to be used anywhere else, to put the decorator into the same file as the class, so that I only have to write the import once and it can't conflict with any other import?

In my real example, I want to write a decorator, which simply changes a flag, which indicates whether a list has been changed or not. Every method in that class, which modifies the list would be decorated using that decorator.

EDIT#1:

After changing the structure of my code, I now have only two participating source code files. Those are the following:

# -*- coding: utf-8 -*-
from AppSettings import AppSettings
from FileManager import FileManager
from decorators.changesvocables import changesvocables
from exceptions.DuplicateVocableException import DuplicateVocableException
from exceptions.UnknownVocableException import UnknownVocableException

__author__ = 'xiaolong'


class VocableManager:

    vocables = []
    search_result = []
    vocables_changed = False

    test_counter = 0
    test_call_counter = 0

    def __init__(self):
        pass

    @classmethod
    @changesvocables
    def remove_vocable(cls, vocable):
        VocableManager.test_call_counter += 1
        if vocable in VocableManager.vocables:
            VocableManager.test_counter += 1
            VocableManager.vocables.remove(vocable)
            # del vocable
        else:
            print(vocable)
            raise UnknownVocableException('Vocable not found.')
        # VocableManager.vocables_changed = True

    @classmethod
    @changesvocables
    def remove_vocable_by_index(cls, index):
        del VocableManager.vocables[index]
        # VocableManager.vocables_changed = True

    @classmethod
    @changesvocables
    def add_vocable(cls, vocable):
        if vocable not in VocableManager.vocables:
            VocableManager.vocables.append(vocable)
        else:
            raise DuplicateVocableException('The vocable already exists.')
        # VocableManager.vocables_changed = True

After that the class still goes on, but there are no other methods with the changesvocables decoration. If wanted, I can add the whole class.

And the decorator:

# -*- coding: utf-8 -*-
from functools import wraps
from VocableManager import VocableManager as VocMan

__author__ = 'xiaolong'


def changesvocables(function):
    @wraps(function)
    def wrapper(*args, **kw):
        result = function(*args, **kw)
        VocMan.vocables_changed = True
        return result
    return wrapper

This causes the following error, when I am trying to run the application:

** (__main__.py:16991): WARNING **: Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-sQwlsgyRi2: Connection refused
Traceback (most recent call last):
  File "/home/xiaolong/development/pycharm-workspace/gtkplus-tool/gtkplustool/__main__.py", line 1, in <module>
    from Application import Application
  File "/home/xiaolong/development/pycharm-workspace/gtkplus-tool/gtkplustool/Application.py", line 8, in <module>
    from VocableManager import VocableManager
  File "/home/xiaolong/development/pycharm-workspace/gtkplus-tool/gtkplustool/VocableManager.py", line 4, in <module>
    from decorators.changesvocables import changesvocables
  File "/home/xiaolong/development/pycharm-workspace/gtkplus-tool/gtkplustool/decorators/changesvocables.py", line 3, in <module>
    from VocableManager import VocableManager as VocMan
ImportError: cannot import name 'VocableManager'

EDIT#3:

Here is a MCVE:

main.py:

# -*- coding: utf-8 -*-
from mcve.ListManager import ListManager

__author__ = 'xiaolong'

if __name__ == '__main__':
    list_manager = ListManager()
    list_manager.add_item('item1')

decorator:

# -*- coding: utf-8 -*-
from functools import wraps
from mcve.ListManager import ListManager as ListMan

__author__ = 'xiaolong'


def changesitems(function):
    @wraps(function)
    def wrapper(*args, **kw):
        result = function(*args, **kw)
        ListMan.vocables_changed = True
        return result
    return wrapper

ListManager:

# -*- coding: utf-8 -*-
from mcve.changesitems import changesitems


class ListManager:

    list_of_items = []

    def __init__(self):
        pass

    @changesitems
    def add_item(self, item):
        if item not in ListManager.list_of_items:
            ListManager.add_item(item)

Same error as before:

Traceback (most recent call last):
  File "/home/xiaolong/development/pycharm-workspace/MCVE/mcve/main.py", line 2, in <module>
    from mcve.ListManager import ListManager
  File "/home/xiaolong/development/pycharm-workspace/MCVE/mcve/ListManager.py", line 2, in <module>
    from mcve.changesitems import changesitems
  File "/home/xiaolong/development/pycharm-workspace/MCVE/mcve/changesitems.py", line 3, in <module>
    from mcve.ListManager import ListManager as ListMan
ImportError: cannot import name 'ListManager'

Process finished with exit code 1

Upvotes: 2

Views: 1564

Answers (1)

BrianO
BrianO

Reputation: 1524

Try:

from Foo import Foo as foo

Now you will have access to the package, as Foo, and to the decorator, as foo.

Upvotes: 2

Related Questions