2

I have a class for a hardware object (here Fridge), and I'd like to automatically create HTTP API routes for a given subset of Fridge's methods, here open and close.

from bottle import Bottle, request

class Fridge:
    def _not_exposed(self):
        print("This one is never called via HTTP")
    def open(self, param1=None, param2=None):
        print("Open", param1, param2)
    def close(self):
        print("Close")
    # + many other methods

f = Fridge()
app = Bottle("")

for action in ["open", "close"]:
    app.route(f"/action/{action}", callback=lambda: (getattr(f, action)(**request.query)))

app.run()    

It works, except that in

...callback=lambda: (getattr(f, action)(**request.query))

action is evaluated when the lambda function is called.

Thus when opening http://127.0.0.1:8080/action/open?param1=123, the lambda is called, and at this time action has the value ... "close" (the last value in the for enumeration), then getattr(f, action) links to f.close instead of f.open. This is not what we want!

Question: in this lambda definition, how to have action evaluated now, and not postponed?

Expected behaviour for the for loop:

app.route("/action/open", callback=lambda: f.open(**request.query)))
app.route("/action/close", callback=lambda: f.close(**request.query)))
James
  • 32,991
  • 4
  • 47
  • 70
Basj
  • 41,386
  • 99
  • 383
  • 673
  • 1
    Use a default arg in the lambda: `lambda action=action: ..`. Default arguments are bound to a function at definition time. – user2390182 Jun 07 '23 at 12:34
  • @user2390182 Oh right. I think you can post this as an answer! – Basj Jun 07 '23 at 12:35
  • 1
    `app.route(f"/action/{action}", callback=(lambda action=action: lambda: getattr(f, action)(**request.query)))` – Obaskly Jun 07 '23 at 12:35
  • I think I will close this as a dupe. This a super common question here ;) – user2390182 Jun 07 '23 at 12:35
  • https://docs.python-guide.org/writing/gotchas/#late-binding-closures This as quick read. – user2390182 Jun 07 '23 at 12:36
  • @Obaskly why a double lambda? It seems this works: `...callback=lambda action=action: (getattr(f, action)(**request.query))` – Basj Jun 07 '23 at 12:48
  • You don't need a "default arg" in this case, just remove the ``lambda``, your ``getattr(f, action)`` is the callable itself – Meitham Aug 30 '23 at 09:57
  • @Meitham Can you give more details, I don't exactly understand your comment :) By what should I replace `callback=lambda: (getattr(f, action)(**request.query))` with your method? – Basj Aug 30 '23 at 10:22

0 Answers0