python-wechaty icon indicating copy to clipboard operation
python-wechaty copied to clipboard

circular import issues

Open huan opened this issue 5 years ago • 8 comments

We need to import modules from here to there because the Types are needed.

Here's a good article to solve this problem: Yet another solution to dig you out of a circular import hole in Python

huan avatar Mar 11 '20 13:03 huan

Fixed by the commit.

huan avatar Mar 11 '20 13:03 huan

This few days, I guess I get into circular import problems. With the same code, Only I get this bug, of which I'am confused.

Below this my analyzation.

  • When I import Contact class in python console. It also occur import problems.
from src.wechaty.user.contact import Contact
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/wujingwujing/PycharmProjects/python-wechaty/src/wechaty/__init__.py", line 7, in <module>
    from .wechaty import Wechaty
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/wujingwujing/PycharmProjects/python-wechaty/src/wechaty/wechaty.py", line 37, in <module>
    from .user import (
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/wujingwujing/PycharmProjects/python-wechaty/src/wechaty/user/__init__.py", line 8, in <module>
    from .message       import Message
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/wujingwujing/PycharmProjects/python-wechaty/src/wechaty/user/message.py", line 41, in <module>
    from .room import Room
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/wujingwujing/PycharmProjects/python-wechaty/src/wechaty/user/room.py", line 33, in <module>
    from .message import Message
  • When I look deep into pytype analyse file, it contains 51 files which need to analyse. Our project only contains 26 files need to be analysed.
  • When I look into .pytype/pyi directory. It contains many *.pyi-1 files, meaning these file have been analysed more then one time.
└── src
    ├── wechaty
    │   ├── accessory.pyi
    │   ├── accessory.pyi-1
    │   ├── config.pyi
    │   ├── types.pyi
    │   ├── types.pyi-1
    │   ├── user
    │   │   ├── Contact.pyi-1
    │   │   ├── Friendship.pyi-1
    │   │   ├── Image.pyi-1
    │   │   ├── Message.pyi-1
    │   │   ├── Room.pyi-1
    │   │   ├── Tag.pyi-1
    │   │   ├── __init__.pyi-1
    │   │   ├── favorite.pyi-1
    │   │   ├── mini_program.pyi
    │   │   └── url_link.pyi
    │   └── wechaty.pyi-1
    └── wechaty_puppet
        ├── contact.pyi
        ├── file_box.pyi
        ├── friendship.pyi
        ├── message.pyi
        ├── mini_program.pyi
        ├── puppet.pyi
        ├── room.pyi
        └── url_link_payload.pyi

I have change a lot of code, so dare not submit code. I will modify the code from master branch, and add my code to the branch little by little.

wj-Mcat avatar Mar 15 '20 02:03 wj-Mcat

Yes, please split the code into different features/branches and then submit into sperate Pull Requests.

That is the best practice of the progress of our developing because we can see what happens via different PRs and make discussion & debugging more affectional and easier.

huan avatar Mar 15 '20 03:03 huan

I met the problem when I did refactoring work on it, it helps much, and as a conclusion, I post the usage guide as below:

  1. If a type is only used for typing hinting but never been referenced in the code, we can feel free to import that classes under a if TYPE_CHECKING clause, like:
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from wechaty import (
        Message,
    )

# so we can safely use the Message class as a type hinting:
def on_message(msg: Message):
    print(msg)

# !!ATTENTION!! but we CANNOT use it as a reference:
def on_message(msg):
    # DONT: if you use it like this way, you are trying to use it as a reference and will possibly got a `Message is not defined` error
    if isinstance(msg, Message):
        print(msg)
    # DONT: instantiate the class is also not allowed in this way.
    msg = Message('hello')

So, before you decide whether to put the import variable inside or outside the TYPE_CHECKING clause, you should be aware that whether you will use the name as a reference or purely as a type hinting usage.

fish-ball avatar Jul 19 '20 14:07 fish-ball

So the problem is solved now, the issue is ok to be closed.

fish-ball avatar Jul 19 '20 14:07 fish-ball

@fish-ball Can you show us your solution ?

# !!ATTENTION!! but we CANNOT use it as a reference:
def on_message(msg):
    # DONT: if you use it like this way, you are trying to use it as a reference and will possibly got a `Message is not defined` error
    if isinstance(msg, Message):
        print(msg)
    # DONT: instantiate the class is also not allowed in this way.
    msg = Message('hello')

My solution is to import the target module at local function. So, it doesn't need to import the target module at the top.

wj-Mcat avatar Jul 20 '20 01:07 wj-Mcat

@wj-Mcat local importing is ok, but it seems that the pylint check will fail.

So I think the first choice is to import it at the top first, without putting it in the TYPE_CHECKING clause.

And the second choice is import at local place, and skip the pylint checking rule.

fish-ball avatar Jul 21 '20 07:07 fish-ball

@fish-ball Of course, if the first choice will cause the circular dependency problems which can't be avoided, I will make the second choice with the skiping annotation.

wj-Mcat avatar Jul 21 '20 07:07 wj-Mcat