Method call in scip/api/__init__.py implements a hand-coded dispatcher. But dispatching is what Flask was invented for: it dispatches an API request to the appropriate method with the @app.route decorator applied to that method. Most of this was copied from PCEX. But that code is 8+ years old, and I don't think it is a good guide to how we should do things. Flask has come a long way since then.
Instead of the call method, I think we can:
- Decorate each API method separately with
@app.route. E.g., decorate the method scip.api.population.population with @app.route("/population"). This is fundamental Flask usage.
- In each API method, you access can query parameters via the
request object. In particular, using request.args.get(qp_name).
- Put parameter checking into each API method. Make it specific to the method. Factor out only the genuinely common parts.
- You might like to leave behind the arg-checking utilities here (
get_required_args, get_keyword_args), and maybe extract a bit of the content of call for checking args. But this all seems a bit laboured to me and I bet we can come up with something quite a bit simpler.
Other thoughts:
- I think
scip/routes.py also largely disappers. @app.after_request probably needs to be kept; it would go wherever add_routes is currently called, or, looking forward, wherever we register the blueprint(s) we create.
- I think Flask provides some extra support for testing. In any case, the tests may need to be refactored a bit.
Notes on Flask:
- There is no need to directly invoke
Response in an API function; Flask does this for you. For example for JSON responses, which is your gig here. You can just return a value and the rest is done automagically.
- Via "blueprints", Flask simplifies a lot of the machinery around registering route handlers with an app. (And it makes the route handlers reusable, bonus.)
- There is more to learn about Flask, but the documentation is fairly good and there is useful advice on SO and elsewhere.
Alternative: The package Connexion wraps all this up very nicely for you, if you have any interest in adopting it. Several PCIC projects use it. It trades off a significant learning curve for simpler Python code and self-documentation.
Method
callinscip/api/__init__.pyimplements a hand-coded dispatcher. But dispatching is what Flask was invented for: it dispatches an API request to the appropriate method with the@app.routedecorator applied to that method. Most of this was copied from PCEX. But that code is 8+ years old, and I don't think it is a good guide to how we should do things. Flask has come a long way since then.Instead of the
callmethod, I think we can:@app.route. E.g., decorate the methodscip.api.population.populationwith@app.route("/population"). This is fundamental Flask usage.requestobject. In particular, usingrequest.args.get(qp_name).get_required_args,get_keyword_args), and maybe extract a bit of the content ofcallfor checking args. But this all seems a bit laboured to me and I bet we can come up with something quite a bit simpler.Other thoughts:
scip/routes.pyalso largely disappers.@app.after_requestprobably needs to be kept; it would go whereveradd_routesis currently called, or, looking forward, wherever we register the blueprint(s) we create.Notes on Flask:
Responsein an API function; Flask does this for you. For example for JSON responses, which is your gig here. You can just return a value and the rest is done automagically.Alternative: The package Connexion wraps all this up very nicely for you, if you have any interest in adopting it. Several PCIC projects use it. It trades off a significant learning curve for simpler Python code and self-documentation.