Question about creating routes

58 views
Skip to first unread message

Laurent Daverio

unread,
Mar 13, 2024, 1:50:07 PMMar 13
to pylons-...@googlegroups.com
Hello list,

it seems that Google is allowing me to post on here again. A couple of weeks ago, I was banned, both from emailing the list, AND from posting on the web group 💩

I am trying to create a series of routes based on lambda functions, something like:

```
config.add_route('doc1.delete_selection', '/doc1/-/delete_selection')
config.add_route('
doc2.delete_selection', '/doc2/-/delete_selection')
config.add_view(
    lambda request: _delete_multi(request, Doc1Resource),
    route_name='doc1.delete_selection', request_method='POST', renderer='json')
config.add_view(
    lambda request: _delete_multi(request, Doc2Resource),
    route_name='doc2.delete_selection', request_method='POST', renderer='json')
```

(I also have `archive_selection` and `unarchive_selection` routes)

I'm under the impression that `config.add_view` can't take lambda functions as arguments, as it tries to resolve their names ("maybe_dotted", "maybe_resolve", etc.). All I can say it that the views seem to be calling the wrong functions. This prevents me from using for loops and lambda for generating the routes in a more concise way :/

Could anyone confirm if I'm correct?

Many thanks,

Laurent.

Jonathan Vanasco

unread,
Mar 13, 2024, 3:34:16 PMMar 13
to pylons-discuss
I'm not sure about lambdas, but this reminded me the tutorial with "discriminators", which goes into some troubleshooting:

I feel like I've encountered something around this concept before , but I can't recall what project it was in to look at code. :/

Mike Orr

unread,
Mar 13, 2024, 4:26:02 PMMar 13
to pylons-...@googlegroups.com
I'm not an expert in this, but I think the maybe-resolve functions
only do lookups when the spec is a string, and pass non-strings
through unchanged. As far as I know lambdas are regular functions, so
I'd expect it to pass them through like view callable functions. Maybe
another exception is occurring, and when Python tries to print the
traceback, it can't find the lambda's source.

I generally use regular functions instead of lambdas, but I realize
this is a stylistic choice and some think otherwise. A partial might
work in your use case:
config.add_view( functools.partial(_delete_multi,
arg2_keyword=Doc2Resource) , ...
> --
> You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to pylons-discus...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/CAB7cU6y56gx3fDEGsdx3qFJKTVPOM5yN1maohu8tEESRnY2ygQ%40mail.gmail.com.



--
Mike Orr <slugg...@gmail.com>

Michael Merickel

unread,
Mar 13, 2024, 5:17:30 PMMar 13
to pylons-...@googlegroups.com
Laurent, what error do you get? I'm not immediately aware of limitations around using lambdas in Pyramid. This example below works fine with a lambda as a view:

from pyramid.config import Configurator
from waitress import serve

config = Configurator()
config.add_route('foo', '/')
config.add_view(lambda req: 'foo', route_name='foo', renderer='string')
app = config.make_wsgi_app()
serve(app, listen='*:8080')


Laurent Daverio

unread,
Mar 13, 2024, 7:36:16 PMMar 13
to pylons-...@googlegroups.com
Thank you everybody, I'll try to set up a working example tomorrow.

Actually you're right, my question was formulated incorrectly, it may not be a question of lambdas. Basically, I was trying to create a series of routes in a reasonably concise way, something like this : 2 types of objects (actes, constats), 2 operations (archive, unarchive), 2 variants (handle one object, or handle a list of ids):

- /actes/{id}/archive
- /actes/{id}/unarchive
- /constats/{id}/archive
- /constats/{id}/unarchive
- /actes/-/archive_selection
- /actes/-/unarchive_selection
- /constats/-/archive_selection
- /constats/-/unarchive_selection

```

factories = {'actes': {'fact': acte_factory,
'rsrc': ActeResource},
'constats': {'fact': constat_factory,
'rsrc': ConstatResource}}

functions = {'archive': (_archive, _archive_multi),
'unarchive': (_unarchive, _unarchive_multi)}

for cat in ('actes', 'constats'):
for op in ('archive', 'unarchive'):

route_name: str = f'api.{cat}.{op}'

config.add_route(route_name, f'/{cat}/{{id}}/{op}', factory=factories[cat]['fact'])
config.add_view(functions[op][0], route_name=route_name, request_method='GET', renderer='json')

config.add_route(route_name + '_selection', f'/{cat}/-/{op}_selection')
config.add_view(
lambda request: functions[op][1](request, factories[cat]['rsrc']),
route_name=route_name + '_selection', request_method='POST', renderer='json')

```

The first 4 routes (those with "{id}" in them) worked fine, but last 4 (those using lambdas) were all mapped to the same function (`_unarchive_multi`) in the end.

In order to make them work, I had to unroll the loops and enumerate everything. Ugly :(

```
config.add_route('api.actes.archive_selection', '/actes/-/archive_selection')
config.add_route('api.constats.archive_selection', '/constats/-/archive_selection')
config.add_view(
lambda request: _archive_multi(request, ActeResource),
route_name='api.actes.archive_selection', request_method='POST', renderer='json')
config.add_view(
lambda request: _archive_multi(request, ConstatResource),
route_name='api.constats.archive_selection', request_method='POST', renderer='json')

config.add_route('api.actes.unarchive_selection', '/actes/-/unarchive_selection')
config.add_route('api.constats.unarchive_selection', '/constats/-/unarchive_selection')
config.add_view(
lambda request: _unarchive_multi(request, ActeResource),
route_name='api.actes.unarchive_selection', request_method='POST', renderer='json')
config.add_view(
lambda request: _unarchive_multi(request, ConstatResource),
route_name='api.constats.unarchive_selection', request_method='POST', renderer='json')
```

Laurent.


Theron Luhn

unread,
Mar 14, 2024, 1:57:01 AMMar 14
to pylons-...@googlegroups.com
That’s a result of the lack of block scoping in Python.  `op` is going to resolve when the lambda first executes, which is always after the loop has finished, and scoped to the containing function so `op` will always be the final loop value.

Easiest workaround is to just wrap the loop contents in a function, so you get a fresh scope for each permutation.

def make_routes(config, cat, op):
    config.add_route…

for cat in ('actes', 'constats'):
    for op in ('archive', 'unarchive’):
       make_routes(config, cat, op)


— Theron



--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pylons-discus...@googlegroups.com.

Laurent Daverio

unread,
Mar 14, 2024, 9:28:49 AMMar 14
to pylons-...@googlegroups.com
Thank you so much Theron, this is spot-on!

So, it was lambda after all (although not Pyramid). You've solved my problem, I can move on to the next 😅

Thanks again,

Laurent.

Reply all
Reply to author
Forward
0 new messages