1

I have a dispatcher in which a dictionary contains keys and each key has a list of methods/functions to call.

operation_request_handlers = {
    'permissionsVARVARVARVARGET': [jwtoken.validate, permission_query],
    'permissionsVARVARVARVARPOST': [jwtoken.validate, permission_set],
    'permissionsVARVARVARVARDELETE': [jwtoken.validate, permission_delete],
}

This works fine if the dispatch target is a function (i.e. permission_query, or if the target is a static method (i.e. jwtoken.validate).

The problem is I can't work out how to dispatch in cases where jwtoken.validate is not a static method.

As far as I understand it, I would first need to instantiate jwtoken then call the validate method on that instance. Is this right? How would I even instantiate the jwtoken class given all I have is jwtoken.validate?

UPDATE: many have commented thank you all. My current thinking is that I should restrict the valid dispatch targets to callables.

Duke Dougal
  • 24,359
  • 31
  • 91
  • 123
  • So what arguments do you require to *create* such an instance? Does the instance state have to vary each time or could just just create *one* instance for the method? – Martijn Pieters Mar 27 '15 at 21:57
  • I pass in an HTTP request object to the target. – Duke Dougal Mar 27 '15 at 21:59
  • That's the method argument, not the class, right? – Martijn Pieters Mar 27 '15 at 21:59
  • @Martjin yes currently I pass an HTTP request to either a function or a static method. I now have need to pass the HTTP request into a method within an instantiated object. – Duke Dougal Mar 27 '15 at 22:01
  • If the method is _not_ static (i.e.: require and instance), you can pass that instance as the first parameter when calling the method as a class function. `some_class.validate(an_instance_of_some_class)`. Or am I wrong ? – Sylvain Leroux Mar 27 '15 at 22:01
  • 1
    Because a method does not live in isolation; they are bound to an instance for a reason. Without any information about the class involved here there is little we can do to help. – Martijn Pieters Mar 27 '15 at 22:01
  • @SylvainLeroux: but then you may as well call the method on the instance.. – Martijn Pieters Mar 27 '15 at 22:02
  • @Martjin I was hoping there was a way, given the some_class.validate to instantiate some_class first and then execute validate on the instance. – Duke Dougal Mar 27 '15 at 22:04
  • @DukeDougal: you could just create the instance and store the bound methods, yes. Then those methods would always be called on that one instance. – Martijn Pieters Mar 27 '15 at 22:05
  • @MartijnPieters Of course, but I assumed this was here an indirect call via some kind of dispatching mechanism. I had something more like that in mind: `handler = { 'action':some_class.validate};` and later on `handler['action'](an_instance_of_some_class)`. Based on the idea the instance will be available when the handler is executed -- but not when it was defined. But I may have misunderstood the OP needs, though. – Sylvain Leroux Mar 27 '15 at 22:06
  • Thanks folks. seems it can't be done in any practical way. I'll need to rethink this approach. – Duke Dougal Mar 27 '15 at 22:08
  • 2
    Before we explore ways to instantiate the class, maybe you can explain what the end goal is and we might be able to come up with something more elegant – Alex W Mar 27 '15 at 22:09
  • @Alex my application receives an HTTP request which is a file upload. I then want to pull that request apart into various components, make some decisions about what type of data it is and then bounce it around various processing functions to complete handling. For example, get_file_from_post_data, save_uploaded_file_to_database, save_uploaded_file_to_fulltextsearch, create_message_record. If done as an object then I just pull apart the data then do the methods. If done as a set of functions I need to pass parameters between all the functions which is annoying. Does this make sense? – Duke Dougal Mar 27 '15 at 22:14

3 Answers3

1

You either need to create your instance up front and store the bound methods, or bind the method to an instance later on.

You'd use the first if all you ever need is the one instance; the methods can then simply be looked up and stored in the mapping:

a_token = jwtoken()
operation_request_handlers = {
    'permissionsVARVARVARVARGET': [a_token.validate, permission_query],
    'permissionsVARVARVARVARPOST': [a_token.validate, permission_set],
    'permissionsVARVARVARVARDELETE': [a_token.validate, permission_delete],
}

This assumes that the jwtoken() class doesn't take any additional arguments.

If you need a new instance each time, you can pass that instance as the first argument of the unbound methods you stored;

operation_request_handlers = {
    'permissionsVARVARVARVARGET': [jwtoken.validate, permission_query],
    'permissionsVARVARVARVARPOST': [jwtoken.validate, permission_set],
    'permissionsVARVARVARVARDELETE': [jwtoken.validate, permission_delete],
}

method, permission = operation_request_handlers[some_key]
a_token = jwtoken()
result method(a_token, *other_arguments)

Whereas looking up a method on an instance produces a bound method (and they'll pass in the instance as the self first argument of the method), looking up the method on the class produces an unbound method (or simply the function if you are using Python 3) and you need to explicitly pass in that first argument.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I'm thinking a solution to the problem is to allow dispatches only to unbound functions and if the function needs a new object then it can instantiate it. – Duke Dougal Mar 27 '15 at 22:31
  • @DukeDougal: you could do that with a lambda; `lambda *args: jwtoken().validate(*args)` would create a new instance each time. Again assuming you don't need to give the class any arguments. – Martijn Pieters Mar 28 '15 at 08:43
0

You can use the im_class special attribute to instantiate an object from a class given a method object of that class:

operation_request_handlers = {
    'permissionsVARVARVARVARGET': [jwtoken.validate, permission_query],
    'permissionsVARVARVARVARPOST': [jwtoken.validate, permission_set],
    'permissionsVARVARVARVARDELETE': [jwtoken.validate, permission_delete],
}
m = operation_request_handlers[my_key][0]    # jwtoken.validate
obj = m.im_class()      # An instance of jwtoken class 
                        # assuming that the constructor takes no arguments
result = getattr(obj, m.__name__)(request)  # Call method
Selcuk
  • 57,004
  • 12
  • 102
  • 110
  • The better name for that attribute is `__class__`; `im_class` is a now-deprecated Python 2 name. – Martijn Pieters Mar 27 '15 at 22:12
  • 1
    ...assuming the class constructor takes no arguments, that is. – martineau Mar 27 '15 at 22:12
  • @MartijnPieters They are not the same thing as `__class__` will return the class of method, for example an `instancemethod`. There is no exact counterpart of `im_class` in Python 3. – Selcuk Mar 27 '15 at 22:17
  • @martineau You are right, but we have to assume something. – Selcuk Mar 27 '15 at 22:18
  • @Selcuk: ah, brain freeze; there are no unbound methods in Python 3 of course. And in Python I got confused with `__func__` here. Time for bed! – Martijn Pieters Mar 27 '15 at 22:18
  • 1
    @selcuk thanks but I'm using Python 3 - this solution is Python 2 only as far as I understand. – Duke Dougal Mar 27 '15 at 22:27
  • @DukeDougal Yes, in Python 3 there is no such thing as an unbound method, but you can check [this answer](http://stackoverflow.com/a/25959545/2011147) for a workaround. – Selcuk Mar 27 '15 at 22:32
0

I'm not really sure what the "right" answer is to this but in the end I made the dispatch target not a method but a class.

Once I did that everything was fine and I could dispatch directly to the class.

Duke Dougal
  • 24,359
  • 31
  • 91
  • 123