2

I'm currently writing some kind of tiny api to support extending module classes. Users should be able to just write their class name in a config and it gets used in our program. The contract is, that the class' module has a function called create(**kwargs) to return an instance of our base module class, and is placed in a special folder. But the isinstance check Fails as soon as the import is made dynamically.

modules are placed in lib/services/name

module base class (in lib/services/service)

class Service:
    def __init__(self, **kwargs):
        #some initialization

example module class (in lib/services/ping)

class PingService(Service):
    def __init__(self, **kwargs):
        Service.__init__(self,**kwargs)
        # uninteresting init

def create(kwargs):
    return PingService(**kwargs)

importing function

import sys
from lib.services.service import Service

def doimport( clazz, modPart, kw, class_check):
    path = "lib/" + modPart
    sys.path.append(path)
    mod = __import__(clazz)
    item = mod.create(kw)

    if class_check(item):
        print "im happy"
        return item

calling code

class_check = lambda service: isinstance(service, Service)
s = doimport("ping", "services", {},class_check)

print s

from lib.services.ping import create

pingService = create({})
if isinstance(pingService, Service):
    print "why this?"

what the hell am I doing wrong

here is a small example zipped up, just extract and run test.py without arguments zip example

Rafael T
  • 15,401
  • 15
  • 83
  • 144
  • 1
    First, if you're using Python 2.x, you should not be using old-style classes. Always do `class Service(object):`, not `class Service:`. The rules for old-style classes are significantly different than for new-style classes, and you really don't want to learn both. – abarnert Jun 18 '13 at 21:39
  • 1
    Second, your code doesn't run at all. For example, that `mod.create(clazzItem)` just raises a `NameError` on `clazzItem`. Please give us an actual stripped-down, runnable example. – abarnert Jun 18 '13 at 21:41
  • first: this has nothing do do with my question, as I tried both. second: I like the rules for old style classes :) – Rafael T Jun 18 '13 at 21:41
  • 2
    What exactly do you like about the rules for old-style classes? Broken multiple inheritance? Dunder methods not working the same way the docs describe? Descriptors not working? – abarnert Jun 18 '13 at 21:44
  • sorry you're right! updated it. for a full example checkout git@github.com:hanez/linspector.git – Rafael T Jun 18 '13 at 21:44
  • That's an entire project. Can you make a self-contained script that illustrates your problem? – Blender Jun 18 '13 at 21:46
  • 1
    Anyway, the most likely problem here is that you end up with two different copies of the `services` module, so your classes inherit from `one_copy_of_services.Service`, while you're testing against `other_copy_of_services.Service`. You can print out `id(services)` at each place (including right after the `import` in each module that you didn't show us) and/or print out `sys.modules` to help debug the problem. – abarnert Jun 18 '13 at 21:46
  • @Dunder that one just can't say myobject.somethingImNotAwareOf, but I guess thats not the point here – Rafael T Jun 18 '13 at 21:47
  • @Dunder it should work know as it is – Rafael T Jun 18 '13 at 21:49
  • I have no idea what those last two replies to a non-existent user are meant to tell anyone. But if you don't either give us an [SSCCE](http://sscce.org) as Blender asked, you will have to do the debugging steps I asked for, or all anyone can do is guess here. (Giving us the entire project doesn't help, because it's not at all clear how to even run the project, or where that `import` function is, etc., and you can't expect anyone to put in the effort required to figure all that out.) – abarnert Jun 18 '13 at 21:53
  • One last comment: The problem might have nothing to do with dynamic import, but rather with mixing up packages and modules. You should not be trying to import something as a top-level module when it's also a member of a package you've imported. There are basically no guarantees as to when they will and won't end up being identical, so you have to be very careful to write code that always works. – abarnert Jun 18 '13 at 21:55
  • @abarnert working on sscce – Rafael T Jun 18 '13 at 21:58
  • @abarnet updated my question with sscce – Rafael T Jun 18 '13 at 22:37

1 Answers1

2

The problem was in your ping.py file. I don't know exactly why, but when dinamically importing it was not accepting the line from service import Service, so you just have to change it to the relative path: from lib.services.service import Service. Adding lib/services to the sys.path could not make it work the inheritance, which I found strange...

Also, I am using imp.load_source which seems more robust:

import os, imp
def doimport( clazz, modPart, kw, class_check):
    path = os.path.join('lib', modPart, clazz + '.py')
    mod = imp.load_source( clazz, path )
    item = mod.create(kw)

    if class_check(item):
        print "im happy"
        return item
Saullo G. P. Castro
  • 56,802
  • 26
  • 179
  • 234
  • syntax error away, main error exists. Plz have a look at my sample – Rafael T Jun 18 '13 at 22:47
  • @RafaelT now it is working, I've updated the answer... it seems to be a weird bug which was solved by using the relative path when importing Service inside ping.py – Saullo G. P. Castro Jun 19 '13 at 09:14
  • sry, didn't had the time to test it until now. I really don't know why, but you get it to work! Was debugging hours yesterday. The first bug I ecnountered in python, and I can't think of a reason for this behavior, as the call to `id(Service)` results in the same id all the time – Rafael T Jun 19 '13 at 20:14