2

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
Zelphir Kaltstahl
  • 5,722
  • 10
  • 57
  • 86

1 Answers1

2

Try:

from Foo import Foo as foo

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

BrianO
  • 1,496
  • 9
  • 12
  • This seems to be a perfectly fine answer and it makes sense. I still somehow can't import the same class, but now I don't understand why anymore. – Zelphir Kaltstahl Sep 19 '15 at 21:24
  • Hmm perhaps I misunderstood. It seems the thing `Foo` in the package `Foo` is *not* the decorator, after all. You're getting the error, I think, because when the offending line `from Foo import Foo` is exec'd for a second time: `Foo` is already imported and no longer denotes a package (or module). So: is it difficult to just change the package/module name? That would eliminate the hair-tearing :) – BrianO Sep 19 '15 at 21:51
  • Just to confirm: you do `from Foo import Foo` in two places -- in the deco's module, and in the class's module -- yes? Have you changed both statements by appending ` as foo`? (and making whatever further changes that entails?) – BrianO Sep 19 '15 at 21:55
  • Firstly, yes, I use the import statement in the class and in the decorator's file. However, I changed only the import in the decorator's file by adding a `as VocMan` while the actual class name is `VocableManager`. I'll try to change both imports next. – Zelphir Kaltstahl Sep 19 '15 at 22:47
  • I don't understand how I can change the package or module name at runtime. Do you suggest such a thing? – Zelphir Kaltstahl Sep 19 '15 at 22:48
  • Noo I didn't mean at runtime, I meant rename it statically, in the filesystem! (and then change your imports accordingly). In short, eliminate the problem of importing `Foo` from `Foo`: change one of the two names. But anyway, ... now that you've changed *both* import statements, how did that go? (I'm groping in the dark as I don't see your source files -- 3 are involved. Several solutions are possible, beyond the fix(es) already mentioned.) – BrianO Sep 19 '15 at 23:19
  • I had to change my structure and I'll add my current structure to the post : ) – Zelphir Kaltstahl Sep 20 '15 at 13:12