QuiteQuiet / PokemonShowdownBot

Pokemon Showdown Chatbot made in Python 3.4
MIT License
5 stars 13 forks source link

TypeError: 'module' object is not iterable #7

Closed ps-deltaruby closed 6 years ago

ps-deltaruby commented 6 years ago

Hey, I'm running your bot into a virtualenv (everything is installed, but I don't have sudo access on this computer) but i'm getting the following error:

+~~~~~~~~~~~~~~~~~~~~~~~~+
|  Pokemon Showdown Bot  |
|      Created by:       |
|      Quite Quiet       |
+~~~~~~~~~~~~~~~~~~~~~~~~+
Loading commands...
Loaded from commands
Traceback (most recent call last):
  File "app.py", line 290, in <module>
    psb = PSBot()
  File "app.py", line 39, in __init__
    self.invoker = CommandInvoker()
  File "/Users/conradscherb/Documents/Python/invoker.py", line 86, in __init__
    self.buildInvokerTable()
  File "/Users/conradscherb/Documents/Python/invoker.py", line 95, in buildInvokerTable
    for command in commands:
TypeError: 'module' object is not iterable

can you help with this?

QuiteQuiet commented 6 years ago

Have you done any changes/added anything to the project? This is not something I can reproduce even in a virtualenv (not that this should change anything).

If not, feel free to add the following to line 109 (correctly indented of course) and paste the printout here.

except TypeError as e:
    traceback.print_tb(e.__traceback__)
    print(e, '\n', modname)
ps-deltaruby commented 6 years ago

Hey, I'm getting a different error now, and no, I haven't edited anything (apart from details.yaml)

(env) Scherbs-MacBook-Air:python conradscherb$ python app.py
+~~~~~~~~~~~~~~~~~~~~~~~~+
|  Pokemon Showdown Bot  |
|      Created by:       |
|      Quite Quiet       |
+~~~~~~~~~~~~~~~~~~~~~~~~+
Loading commands...
'Loaded from commands'
 ' File "/Users/conradscherb/Documents/Python/invoker.py", line 95, in buildInvokerTable'
    'for command in commands:'
'module' object is not iterable '
 pip
Traceback (most recent call last):
  File "app.py", line 290, in <module>
    psb = PSBot()
  File "app.py", line 39, in __init__
    self.invoker = CommandInvoker()
  File "/Users/conradscherb/Documents/Python/invoker.py", line 86, in __init__
    self.buildInvokerTable()
  File "/Users/conradscherb/Documents/Python/invoker.py", line 91, in buildInvokerTable
    importedModule = importlib.import_module(modname)
  File "/Users/conradscherb/Documents/Python/env/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/conradscherb/Documents/Python/pip/_vendor/distro.py", line 41, in <module>
    raise ImportError('Unsupported platform: {0}'.format(sys.platform))
ImportError: Unsupported platform: darwin
QuiteQuiet commented 6 years ago

It almost looks like you're iterating through global packages as well as the local with _iterPackages rather than just local which was the intention (and how it works on the Windows/Linux platforms that I've tried). I don't have a Mac to try this on, but this seems to be a difference in pkgutil on OSX, because pip shouldn't be a local module on path '.'...

ps-deltaruby commented 6 years ago

hm okay, would you have any suggestions in order to fix this?

QuiteQuiet commented 6 years ago

Can you do this in the terminal on the same path as app.py and paste the output here?

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path = ['.'], onerror = lambda x: None):
    print('importer:', importer, ', modname:', modname, ', ispkg:', ispkg)

It might be the case that your current path where pkgutil look is different from the path where app.py is.

ps-deltaruby commented 6 years ago

https://hastebin.com/ehahupumec.py is the output

ps-deltaruby commented 6 years ago

This is the output with everything redone in Python3

https://hastebin.com/ujazikagul.py

QuiteQuiet commented 6 years ago

I've spent parts of the New Years weekend trying to replicate this but I've not been able to manage.

Follow-up question though: did you place the project in the same folder as Python is installed in?

QuiteQuiet commented 6 years ago

Alternatively, try:

diff --git a/invoker.py b/invoker.py
index 3ed7a37..fdd670c 100644
--- a/invoker.py
+++ b/invoker.py
@@ -92,17 +92,20 @@ class CommandInvoker:
             try:
                 # Look through the module and see if any commands have been defined
                 commands = getattr(importedModule, 'commands')
-                for command in commands:
-                    # build the invoker table for each command
-                    for trigger in command.cmdTriggers:
-                        if trigger in self.cmdInvokers:
-                            print('{} already exists as a command'.format(trigger))
-                            continue
-                        if trigger.startswith('!') and trigger[1:] in self.cmdInvokers:
-                            command.hasBroadcastAlt = True
-                            continue
-                        self.cmdInvokers[trigger] = command
-                print('Loaded from {}'.format(modname))
+                try:
+                    for command in commands:
+                        # build the invoker table for each command
+                        for trigger in command.cmdTriggers:
+                            if trigger in self.cmdInvokers:
+                                print('{} already exists as a command'.format(trigger))
+                                continue
+                            if trigger.startswith('!') and trigger[1:] in self.cmdInvokers:
+                                command.hasBroadcastAlt = True
+                                continue
+                            self.cmdInvokers[trigger] = command
+                    print('Loaded from {}'.format(modname))
+                except TypeError as e:
+                    print('Module {} had an incompatible type for `commands`; type(commands) == {}'.format(modname, type(commands)))
             except AttributeError:
                 # Module contains no commands, this is expected and should be ignored
                 pass

If the above does solve the issue (the issue here being that it's trying to load commands from modules that isn't included in the project, and failing) then let me know.

QuiteQuiet commented 6 years ago

Apparently there's a follow-up problem of importlib.import_module trying to import modules that the current OS doesn't support/have a package for which causes other issues but is unrelated to the original problem but shows up because of the above "fix".

Because I cannot find a reliable way to replicated it, here's a theoretical solution to the problem:

    def buildInvokerTable(self):
        print('Loading commands...')
        failedtrees = []
        for importer, modname, ispkg in self._iterPackages():
            # skip subtrees of a failed import
            if [subtree for subtree in failedtrees if modname.startswith(subtree)]: continue
            try:
                importedModule = importlib.import_module(modname)
                try:
                    # Look through the module and see if any commands have been defined
                    commands = getattr(importedModule, 'commands')
                    try:
                        for command in commands:
                            # build the invoker table for each command
                            for trigger in command.cmdTriggers:
                                if trigger in self.cmdInvokers:
                                    print('{} already exists as a command'.format(trigger))
                                    continue
                                if trigger.startswith('!') and trigger[1:] in self.cmdInvokers:
                                    command.hasBroadcastAlt = True
                                    continue
                                self.cmdInvokers[trigger] = command
                        print('Loaded from {}'.format(modname))
                    except TypeError as e:
                        # The module had a `commands` entry that was of an unexpected type
                        # Mainly a safeguard towards importing native Python packages by mistake
                        print('Module {} had an incompatible type for `commands`; type(commands) == {}; Skipping subtree...'.format(modname, type(commands)))
                        failedtrees.append(modname)
                except AttributeError:
                    # Module contains no commands, this is expected and should be ignored
                    pass
            except ImportError as e:
                # Something went horribly wrong
                print(modname)
                traceback.print_tb(e.__traceback__)
                print(e)
QuiteQuiet commented 6 years ago

I got a confirmation that the above did resolve the issue, so closed for now.