tags:

views:

154

answers:

2

hy all, I have the following "wrong" dispatcher:

def _load_methods(self):
 import os, sys, glob
 sys.path.insert(0, 'modules\commands')
 for c in glob.glob('modules\commands\Command*.py'):
  if os.path.isdir(c):
   continue
  c = os.path.splitext(c)[0]
  parts = c.split(os.path.sep )
  module, name = '.'.join( parts ), parts[-1:]
  module = __import__( module, globals(), locals(), name )
  _cmdClass = __import__(module).Command
  for method_name in list_public_methods(_cmdClass):
   self._methods[method_name] = getattr(_cmdClass(), method_name)
 sys.path.pop(0)

It produces the following error:

ImportError: No module named commands.CommandAntitheft

where Command*.py is placed into modules\commands\ folder

can someone help me?

One Possible solution (It works!!!) is:

    def _load_methods(self):
 import os, sys, glob, imp

 for file in glob.glob('modules/commands/Command*.py'):
  if os.path.isdir(file):
   continue
  module = os.path.splitext(file)[0].rsplit(os.sep, 1)[1]
  fd, filename, desc = imp.find_module(module,
    ['./modules/commands'])
  try:
   _cmdClass = imp.load_module( module, fd, filename, desc).Command
  finally:
   fd.close()

  for method_name in list_public_methods(_cmdClass):
   self._methods[method_name] = getattr(_cmdClass(), method_name)

It still remains all risks suggested by bobince (tanks :-) )but now I'm able to load commands at "runtime"

+1  A: 

sys.path.insert(0, 'modules\commands')

It's best not to put a relative path into sys.path. If the current directory changes during execution it'll break.

Also if you are running from a different directory to the script it won't work. If you want to make it relative to the script's location, use file.

Also the ‘\’ character should be escaped to ‘\\’ for safety, and really it ought to be using os.path.join() instead of relying on Windows path rules.

sys.path.insert(0, os.path.abspath(os.path.join(__file__, 'modules')))

sys.path.pop(0)

Dangerous. If another imported script has played with sys.path (and it might), you'll have pulled the wrong path off. Also reloads of your own modules will break. Best leave the path where it is.

module, name = '.'.join( parts ), parts[-1:]

Remember your path includes the segment ‘modules’. So you're effectively trying to:

import modules.commands.CommandSomething

but since ‘modules.commands’ is already in the path you added to search what you really want is just:

import CommandSomething

__import__( module, globals(), locals(), name )

Also ‘fromlist’ is a list, so it should be ‘[name]’ if you really want to have it write ‘CommandSomething’ to your local variables. (You almost certainly don't want this; leave the fromlist empty.)

_cmdClass = __import__(module).Command

Yeah, that won't work, module is a module object and __import__ wants a module name. You already have the module object; why not just “module.Command”?

My reaction to all this is simple: Too Much Magic.

You're making this overly difficult for yourself and creating a lot of potential problems and fragility by messing around with the internals of the import system. This is tricky stuff even for experienced Python programmers.

You would almost certainly be better off using plain old Python modules which you import explicitly. Hard-coding the list of commands is really no great hardship; having all your commands in a package, with __init__.py saying:

__all__= ['ThisCommand', 'ThatCommand', 'TheOtherCommand']

may repeat the filenames once, but is much simpler and more robust than a surfeit of magic.

bobince
I'm not necessarily sure that hard-coding things is the best solution (that can get to be a maintenance nightmare as a project grows), but overall I think this is a good answer.
Jason Baker
+1  A: 

Do you actually need to import things as modules? If you're just loading code from arbitrary positions in the filesystem, then rather than fiddling with the module path etc, you could just use execfile.

ie.

for file in glob.glob('modules/commands/Command*.py'):
    if os.path.isdir(file):
        continue

    moddict={}
    execfile(file, moddict)
    _cmdClass = moddict['Command']
    ...
Brian