How to invoke a class method from trigger?

Posted by Cadari on Tue 08 Jul 2008 07:53 PM — 5 posts, 20,067 views.

#0
Hello. How would you call a class method from a trigger?

class MyClass:
    def __init__(self):
        pass
    def trigger_procedure(self, name, line, wildcards):
        world.Note('Trigger fired.')
        
obj = MyClass()

<triggers>
  <trigger
   enabled="y"
   match="^$"
   regexp="y"
   script="obj.trigger_procedure"
   sequence="100"
  >
  </trigger>
</triggers>

The code above will yield an error "procedure not found". I can register my method in globals...

globals()['obj.trigger_procedure'] = obj.trigger_procedure

...and it will work, but that's just unacceptable. Any way to solve this? Thank you.
Netherlands #1
I don't think it is possible.

MUSHclient looks for a function 'obj.trigger_procedure' in the global namespace, which doesn't exist. What you want is that MUSHclient queries the trigger_procedure from the 'obj' object, tells Python to call it with Python-regulations (self first), and then to tack on the rest of the parameters.

Maybe you could workaround this with a combination of annotations and importing the functions of your classes into the global namespace, but in annotated form. I know this sounds a bit vague, as I'm not sure how to go about it, but it should technically be possible. I think, anyhow.
#2
Seems I have to use the ugly approach of doing things this way:

ids = []

class MyClass:
    def __init__(self, name):
        ids.append(id(self))
        self.name = name
    def trigger_procedure(self, name, line, wildcards):
        world.Note('Trigger fired.')
        
myobj = MyClass('myobj')

gc.collect()

for obj in gc.get_objects():
    if id(obj) in ids:
        method_name, method_obj = inspect.getmembers(obj, inspect.ismethod)[1:][0]
        globals()['%s.%s' % (obj.name, method_name)] = method_obj

Or a bit less ugly:

obj_names = []

class MyClass:
    def __init__(self, name):
        self.name = name
        obj_names.append(name)
    def trigger_procedure(self, name, line, wildcards):
        world.Note('Trigger fired.')
        
myobj = MyClass('myobj')

for obj_name in obj_names:
    exec('method_name, method_obj = inspect.getmembers(%s, inspect.ismethod)[1:][0]' % obj_name)
    globals()['%s.%s' % (obj_name, method_name)] = method_obj

At least until I find another way.
Amended on Fri 11 Jul 2008 11:19 AM by Cadari
Netherlands #3
Rather than bruteforcing exec in the way you are, I think you should be able to do the same thing with getattr()? I'm only looking at your code briefly and not fully understanding the details of it (not too familliar with the inspect module) but exec() is plain nasty imho.
#4
The exec version is ugly, I agree. As is the whole approach. But I always thought that there's no way to get an object by its name without exec() or eval(), no?

It can be done storing objects themselves, not their names:

objs = []

class MyClass:
    def __init__(self, name):
        self.name = name
        objs.append(self)
    ...
        
myobj = MyClass('myobj')

for obj in objs:
    method_name, method_obj = inspect.getmembers(obj, inspect.ismethod)[1:][0]
    globals()['%s.%s' % (obj.name, method_name)] = method_obj

But, as I said, I don't like any of these versions.