On Thursday, January 5, 2017 at 7:04:56 AM UTC-8, Anthony wrote:
>
> But from the other side of things, I guess the question is, if we can do 
>> that, is there ever any reason to use the "service" mechanism at all?  Or 
>> can request.restful do everything service can do, and more?
>>
>
> I suppose their use cases are overlapping. If @request.restful works for 
> you in this case, then use it. The @service mechanism lets you easily 
> expose functions for use in remote procedure calls, including via 
> standardized RPC protocols. The @service decorators also automatically 
> return the correct response format, whereas @request.restful requires the 
> use of the generic views (disabled by default) to automatically generate 
> different response formats (though it will automatically generate JSON if 
> the request specifies "application/json").
>
> Anthony
>


I guess the request.restful approach works better for me at this point.  Is 
there a way to tell web2py to enable generic views only for restful 
controller functions, and disable them otherwise?

In continuing to explore both approaches, I gained a better understanding 
of my problem with the service approach, and I want to share it here 
because I think it is worth thinking about in terms of the design of 
web2py.  The problem I have with the service approach is that it seems to 
make impossible to use a decorator to wrap a service controller function in 
order to add or remove arguments, because the service function will "block" 
web2py from seeing what arguments the real service function accepts, 
causing web2py to pass the wrong arguments.

There is a certain Python idiom that goes something like this:

def accept_extra_arg(func):
    def wrapper(extra_arg, *args, **kwargs):
        # maybe do something with extra_arg here
        result = func(*args, **kwargs)
        # maybe do something with extra_arg here
        return result
    return wrapper

@accept_extra_arg
    def foo(x, y):
        return x+y

The idea is that the accept_extra_arg decorator allows you to wrap your 
function with a handler that accepts an extra argument which the function 
does not see, but which is used to preprocess the function's arguments 
and/or postprocess its result.  In the case of web APIs, for instance, this 
is a natural way to do something like write a series of API functions that 
require an API key to be used: you can have a @requires_key decorator that 
wraps functions, allowing the function itself to be "pure" and just 
concentrate on returning the data, without having to handle the checking of 
the API key.  There is a related idiom which is the reverse, involving 
writing a decorator that accepts an argument and returns a decorated 
function accepting *fewer* arguments that the original, allowing a behavior 
akin to functools.partial, by which some arguments are specified in advance 
via the decorator, rather than being passed on each call to the decorated 
function.

The web2py service mechanism breaks this idiom.  The problem is that 
because the wrapped function is supposed to be agnostic as to the arguments 
of the function it wraps, it just accepts *args and **kwargs (in addition 
to its extra argument).  This means that web2py won't pass it anything.  
This kind of breaks the RPC idea that calling the function internally 
should be the same as calling it via the network API: if my function wants 
to use *args and/or **kwargs, web2py apparently doesn't provide a way for 
me to use it in a seamless manner for internal vs external calls.

I *think* I have been able to get around this with request.restful, because 
it passes everything along without trying to match the arguments.  But it 
is somewhat annoying for the case of read-only APIs because of the extra 
layer of indirection required (returning a function that returns the data, 
rather than just returning the data).

What I really want is the ability to write an arbitrary function, accepting 
arbitrary arguments.  I want the ability to wrap that function with 
decorators that absorb or add arguments as I please.  And then I want to be 
able to expose that arbitrarily wrapped function as a service endpoint 
(presumably by wrapping the function with a web2py decorator as the last 
one).  I don't want any of the decorators nor the original function ever to 
have to worry about *how* the arguments are being passed (e.g., 
positionally or by keyword), and I don't want any function in the chain to 
ever have to worry about the number or names of any arguments other than 
the ones it explicitly processes from the arguments passed to it.  So, 
basically, I want to be able to use decorators the same way I would use 
them in ordinary Python code, and not have web2py step in at some point and 
disrupt the process by which decorated functions pass their arguments to 
the functions they wrap.

Now, that's not to say that I don't appreciate the tools web2py does 
offer.  I've found them quite powerful.  And for now I think I can make 
things work with the restful decorator.  But just mention above to maybe be 
considered for future web2py development.  The ability to manipulate 
function signatures with decorators in a way that remains transparent from 
the perspective of the wrapped functions is a really nice feature of 
Python, and the ability to use varargs is powerful too, and I think it 
would be good for web2py to allow such functions to be exposed without 
sacrificing those features of Python.

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to