Re: [flask] Let me bounce an idea off of you....
- From:
- Zahari Petkov
- Date:
- 2010-11-10 @ 14:57
Hi Jonas,
On Wed, Nov 10, 2010 at 4:16 PM, Jonas Galvez <jonasgalvez@gmail.com> wrote:
> My dear fellow Flaskers, I'm that guy with an unhealthy obsession with the
> with statement...
:)
> So I've been working on a small Flask extension (well, two) that will let
> you write the Arc Challenge[1] like the following:
> from flask import Flask, request, session, redirect, g
> from flaskext.html5witch import html
> from flaskext.httpwitch import httpwitch
> with httpwitch:
> app = Flask(__name__)
> with app.route('/said'):
> def get():
> if session.get('foo'):
> return 'You said %s.' % session['foo']
> else:
> with html.form:
> html.input(None, name='foo')
> html.input(None, type='submit')
> return html
> def post():
> if request.form.get('foo'):
> session['foo'] = request.form['foo']
> return html.a('Click here.', href='/said')
> else:
> return redirect('/said')
> Does that look good to you? Am I wasting my time? It's tricky setting the
> handlers that way, and I still need to do a lot of tests, but so far the
> basic idea works. I'm planning on pushing it all out to Github over the
> weekend...
I like that you are eager to explore new frontiers with the with
statement and runpy. I see that going in a direction as a fancy
micro-framework on top of Flask if prove to be efficient, or at least
a showcase of Python new features and robustness. It will be
interesting to watch that evolving, especially where more complex
things are involved. I am not sure how that complexity can be managed
this way, since this is something completely different to what we are
accustomed -- whether it will be just nested `with`s with some magic
for managing modularization, or something else, but that is the fun
part for you to explore :)
I would just suggest that you provide a couple of nice and easy to
understand examples as you go further.
Looking forward to see more from you,
Zahari
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 15:22
Zahari Petkov wrote:
> I like that you are eager to explore new frontiers with the with
> statement and runpy. I see that going in a direction as a fancy
> micro-framework on top of Flask if prove to be efficient, or at least
> a showcase of Python new features and robustness. It will be
> interesting to watch that evolving, especially where more complex
> things are involved. I am not sure how that complexity can be managed
> this way, since this is something completely different to what we are
> accustomed -- whether it will be just nested `with`s with some magic
> for managing modularization, or something else, but that is the fun
> part for you to explore :)
That's awesome to hear, it gets lonely sometimes in the "let's try and
do things differently" land :)
I originally intended to adapt this directly into a Flask fork, but
I've noticed Armin is kind of conservative[1] about idiom
introductions so I thought I'd make it an extension first. It might
end up a cleaner implementation this way too. Of course it's slowly
becoming kind of my dream to see this built into Flask... ;)
The implementation, like I said, is kind of hacky, but the end result
are the same handlers defined at boot time, so it will definitely not
cause any impact on performance. It's just a fancy way of setting
handlers and loading them from files.
-- Jonas
[1] https://github.com/mitsuhiko/flask/pull/122
Re: [flask] Let me bounce an idea off of you....
- From:
- danjac354@gmail.com
- Date:
- 2010-11-10 @ 14:26
I kind of like the pattern, but would it be easier to have in a class ?
e.g.
@app.route("/said/")
class Said(Handler):
def GET():
....
def POST():
....
of course that would necessitate class decorators, so a pre-2.6
pattern might use url_map directly (for example).
The Handler class would have a __call__ method to switch to GET/POST etc.
On 10 November 2010 14:16, Jonas Galvez <jonasgalvez@gmail.com> wrote:
> My dear fellow Flaskers, I'm that guy with an unhealthy obsession with the
> with statement...
> So I've been working on a small Flask extension (well, two) that will let
> you write the Arc Challenge[1] like the following:
> from flask import Flask, request, session, redirect, g
> from flaskext.html5witch import html
> from flaskext.httpwitch import httpwitch
> with httpwitch:
> app = Flask(__name__)
> with app.route('/said'):
> def get():
> if session.get('foo'):
> return 'You said %s.' % session['foo']
> else:
> with html.form:
> html.input(None, name='foo')
> html.input(None, type='submit')
> return html
> def post():
> if request.form.get('foo'):
> session['foo'] = request.form['foo']
> return html.a('Click here.', href='/said')
> else:
> return redirect('/said')
> Does that look good to you? Am I wasting my time? It's tricky setting the
> handlers that way, and I still need to do a lot of tests, but so far the
> basic idea works. I'm planning on pushing it all out to Github over the
> weekend...
> -- Jonas
> [1] http://www.paulgraham.com/arcchallenge.html
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 14:34
danjac354@gmail.com wrote:
> I kind of like the pattern, but would it be easier to have in a class ?
>
> e.g.
>
> @app.route("/said/")
> class Said(Handler):
> def GET():
> ....
> def POST():
> ....
I think that should definitely be possible to do, but not mandatory.
The main motivation is to avoid the need to /name/ things (functions,
classes) when I just need to run a block of code (like you can with
Sinatra in Ruby).
Also, I'm thinking of adding a module function to httpwitch that will
let you load all the .py files in a single directory and pre-embed
"app", "request", "session" in the globals dictionary. This feature
would be dependent on Python 2.7's runpy.run_path() function, but it
would eliminate the need to add a lot of boilerplate code in isolated
files defining handlers.
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 15:18
On Wed, 2010-11-10 at 12:34 -0200, Jonas Galvez wrote:
> I think that should definitely be possible to do, but not mandatory.
> The main motivation is to avoid the need to /name/ things (functions,
> classes) when I just need to run a block of code (like you can with
> Sinatra in Ruby).
Where are you naming your endpoints?
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 15:23
Dag Odenhall wrote:
> Where are you naming your endpoints?
What you mean? It still uses (a slightly modified) app.route() to set
the routes...
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 16:02
On Wed, 2010-11-10 at 13:23 -0200, Jonas Galvez wrote:
> Dag Odenhall wrote:
> > Where are you naming your endpoints?
>
> What you mean? It still uses (a slightly modified) app.route() to set
> the routes...
>
> -- Jonas
Werkzeug is built around the concept of "endpoints" which in Flask are
the viewfunc.__name__s. It's what you pass to url_for() for example.
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 16:12
Dag Odenhall wrote:
> Werkzeug is built around the concept of "endpoints" which in Flask are
> the viewfunc.__name__s. It's what you pass to url_for() for example.
Ah, right. I hadn't thought about that but think I can address that
with the following:
with app.route('/users') as users:
def get():
pass
def post():
pass
And it would automatically generate endpoints like get_users, post_users.
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 16:14
> with app.route('/users') as users:
> def get():
> pass
> def post():
> pass
>
> And it would automatically generate endpoints like get_users, post_users.
With the current test code, the endpoints ended up being simply "get"
and "post", which is a bug. I'll change that before pushing to GitHub.
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 16:32
On Wed, 2010-11-10 at 14:14 -0200, Jonas Galvez wrote:
> > with app.route('/users') as users:
> > def get():
> > pass
> > def post():
> > pass
> >
> > And it would automatically generate endpoints like get_users, post_users.
>
> With the current test code, the endpoints ended up being simply "get"
> and "post", which is a bug. I'll change that before pushing to GitHub.
>
> -- Jonas
Does Python really let you know the name of the variable a context
manager yields to, without AST parsing?
FWIW this kind of APIs is what made me stop doing Ruby. I don't mind it
as an extension but big -1 from me for it going in the core. It's
difficult to reason about, it makes view functions very special instead
of nothing special. I don't see any particular gain except the
subjective prettiness of a DSL (ignoring all the issues with DSLs). How
does it even work, are you hacking into the frame to get the functions
or what?
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 17:39
Dag Odenhall wrote:
> FWIW this kind of APIs is what made me stop doing Ruby.
BTW, I too am a Pythonista who wandered into the Ruby land for some
time and have stopped using it ever since. There are many things that
I like about Ruby, but in order of importance, my reasons for leaving:
1) poor performance (even in 1.9) when compared to Python, 2) the
community (effing hipsters, man) and 3) no indentation-based blocks (I
hate all these 'end's).
I also share your concern about using nasty hacks, but in this case
particularly, I will keep my argument that the way the frame object is
being used in httpwitch is harmless.
Integration with the Flask core would involve simply turning
app.route() into a class and adapting the __enter__() and __exit__()
methods from httpwitch.route_context. It's not that complicated :)
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 17:15
Dag Odenhall wrote:
> Does Python really let you know the name of the variable a context
> manager yields to, without AST parsing?
>
> FWIW this kind of APIs is what made me stop doing Ruby. I don't mind it
> as an extension but big -1 from me for it going in the core. It's
> difficult to reason about, it makes view functions very special instead
> of nothing special. I don't see any particular gain except the
> subjective prettiness of a DSL (ignoring all the issues with DSLs). How
> does it even work, are you hacking into the frame to get the functions
> or what?
I /am/ hacking into the frame to get the function, but that's
harmless, because it only happens once at boot time. Once it gets the
function, then it simply uses add_url_rule() and that's done.
It makes 'get', 'post', 'put', 'delete' and 'options' reserverd words tho.
Here's the initial implementation, it's only 24 lines of code:
https://github.com/galvez/flask-httpwitch/blob/master/httpwitch.py
The endpoint issue is fixed, btw. I can just compare globals() before
and after using the context manage to get the variable defined in the
"as" clause.
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 17:53
On Wed, 2010-11-10 at 15:15 -0200, Jonas Galvez wrote:
> I /am/ hacking into the frame to get the function, but that's
> harmless, because it only happens once at boot time. Once it gets the
> function, then it simply uses add_url_rule() and that's done.
>
> It makes 'get', 'post', 'put', 'delete' and 'options' reserverd words
> tho.
getframe isn't portable to other implementations.
>
> Here's the initial implementation, it's only 24 lines of code:
>
> https://github.com/galvez/flask-httpwitch/blob/master/httpwitch.py
>
> The endpoint issue is fixed, btw. I can just compare globals() before
> and after using the context manage to get the variable defined in the
> "as" clause.
Which can break if something uses the global statement.
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 17:27
Here's how to use it as-is:
from httpwitch import httpwitch
from flask import Flask
app = Flask(__name__)
witchyapp = httpwitch(app)
with witchyapp.route('/omg') as omg:
def get():
return 'OMG IT WORKS!'
app.run()
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 18:09
On Wed, 2010-11-10 at 15:27 -0200, Jonas Galvez wrote:
> Here's how to use it as-is:
>
> from httpwitch import httpwitch
> from flask import Flask
>
> app = Flask(__name__)
> witchyapp = httpwitch(app)
>
> with witchyapp.route('/omg') as omg:
> def get():
> return 'OMG IT WORKS!'
>
> app.run()
>
> -- Jonas
I suppose one thing I dislike about it is the procedural feel of it
compared to Flask's declarative feel. Not the most constructive
critique, I'll admit.
Using with like this feels like a hack, though I've had no objections to
the way Logbook uses it and that's somewhat similar. Your exact solution
though is quite hacky, maybe better:
with app.route('omg', '/omg') as method:
if method.get:
return 'OMG IT WORKS!'
Other ideas:
app.route(omg='/omg')
# Though route() takes some kwargs that'd need to be accessible
# from somewhere else to avoid name collisions
app.route.omg('/omg')
# Too metamagic/Rubyesque for me though
The above use of if method.get though probably don't work well or at all
without hacks because we can't defer the execution. Maybe something
like,
@app.routes
def routes():
with ...
But then we might as well just do if's to check if a route matches a
rule ourself, imperatively. This is also too different from Flask or
Werkzeug that it should probably, if at all, be a separate framework.
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 18:53
Dag Odenhall wrote:
> I suppose one thing I dislike about it is the procedural feel of it
> compared to Flask's declarative feel. Not the most constructive
> critique, I'll admit.
>
> Using with like this feels like a hack, though I've had no objections to
> the way Logbook uses it and that's somewhat similar. Your exact solution
> though is quite hacky [...]
I have the same feeling, trust me.
I can see you like the syntax, you just don't like the fact that it a)
uses currentframe() and b) imposes a procedural style by restricting
the definition of variables in the global space during the handler
definition process.
The latter is not too much of a practical concern to me, since I'll
hardly ever need to define a variable within the "with" context when
setting the handlers.
But the biggest problem is the currentframe() dependency. What I
hadn't realized tho, before this little chat, is that the dictionary
globals() returns is a persistent reference to that execution
context's globals, not just a snapshot. So I managed to remove the
need for currentframe() altogether by simply taking globals() in the
httpwitch constructor. Here's the commit:
https://github.com/galvez/flask-httpwitch/commit/0a2f73c7a2f2826f8960e41acd31f27020ed7f84
...
app = Flask(__name__)
witchyapp = httpwitch(app, globals())
...
How about that? :)
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 19:21
On Wed, 2010-11-10 at 16:53 -0200, Jonas Galvez wrote:
> Dag Odenhall wrote:
> > I suppose one thing I dislike about it is the procedural feel of it
> > compared to Flask's declarative feel. Not the most constructive
> > critique, I'll admit.
> >
> > Using with like this feels like a hack, though I've had no objections to
> > the way Logbook uses it and that's somewhat similar. Your exact solution
> > though is quite hacky [...]
>
> I have the same feeling, trust me.
>
> I can see you like the syntax, you just don't like the fact that it a)
> uses currentframe() and b) imposes a procedural style by restricting
> the definition of variables in the global space during the handler
> definition process.
>
> The latter is not too much of a practical concern to me, since I'll
> hardly ever need to define a variable within the "with" context when
> setting the handlers.
>
> But the biggest problem is the currentframe() dependency. What I
> hadn't realized tho, before this little chat, is that the dictionary
> globals() returns is a persistent reference to that execution
> context's globals, not just a snapshot. So I managed to remove the
> need for currentframe() altogether by simply taking globals() in the
> httpwitch constructor. Here's the commit:
>
>
https://github.com/galvez/flask-httpwitch/commit/0a2f73c7a2f2826f8960e41acd31f27020ed7f84
>
> ...
> app = Flask(__name__)
> witchyapp = httpwitch(app, globals())
> ...
>
> How about that? :)
>
> -- Jonas
Doesn't that break as soon as you add globals after witchyapp, don't you
need to pass globals() to every app.route() rather?
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-10 @ 19:30
Dag Odenhall wrote:
> Doesn't that break as soon as you add globals after witchyapp, don't you
> need to pass globals() to every app.route() rather?
No, that's what I was trying to say. The reference returned by
globals() is persistent. Awesome, isn't it? This makes me feel a lot
less guilty from using this pattern.
-- Jonas
Re: [flask] Let me bounce an idea off of you....
- From:
- Dag Odenhall
- Date:
- 2010-11-10 @ 20:16
On Wed, 2010-11-10 at 17:30 -0200, Jonas Galvez wrote:
> Dag Odenhall wrote:
> > Doesn't that break as soon as you add globals after witchyapp, don't you
> > need to pass globals() to every app.route() rather?
>
> No, that's what I was trying to say. The reference returned by
> globals() is persistent. Awesome, isn't it? This makes me feel a lot
> less guilty from using this pattern.
>
> -- Jonas
Other than the utter hackiness of it eh? ;)
I wonder if globals() is guaranteed to be the same reference in other
implementations.
Re: [flask] Let me bounce an idea off of you....
- From:
- Jonas Galvez
- Date:
- 2010-11-11 @ 06:09
Dag Odenhall wrote:
> Other than the utter hackiness of it eh? ;)
>
> I wonder if globals() is guaranteed to be the same
> reference in other implementations.
Oh snap. Just tried it with PyPy, globals() is not persistent in it.
-- Jonas