librelist archives

« back to archive

Werkzeug routing

Werkzeug routing

From:
Davide Muzzarelli
Date:
2010-08-27 @ 13:35
Hi,
is it possible to use the Werkzeug routing instead the Flask one?

I tried to replace the Flask.url_map() with my map and the 
Flask.dispatch_request() method.

Replacing the url_map is easy because I create a new Map() object, after the 
creation of the app, with the rules in it.

For the dispatch_request I have problems because I do not find a way about how 
to replace the call to view_functions instead the Map() call.

There is someone that have a suggestion?

(sorry for my bad english)

Davide Muzzarelli

Re: [flask] Werkzeug routing

From:
Dag Odenhall
Date:
2010-08-27 @ 14:36
fre 2010-08-27 klockan 15:35 +0200 skrev Davide Muzzarelli:
> Hi,
> is it possible to use the Werkzeug routing instead the Flask one?
> 
> I tried to replace the Flask.url_map() with my map and the 
> Flask.dispatch_request() method.
> 
> Replacing the url_map is easy because I create a new Map() object, after the 
> creation of the app, with the rules in it.
> 
> For the dispatch_request I have problems because I do not find a way about how 
> to replace the call to view_functions instead the Map() call.
> 
> There is someone that have a suggestion?
> 
> (sorry for my bad english)
> 
> Davide Muzzarelli

Does this satisfy your needs?

http://flask.pocoo.org/docs/api/#flask.Flask.add_url_rule

Re: [flask] Werkzeug routing

From:
Davide Muzzarelli
Date:
2010-08-27 @ 14:44
In data venerdì 27 agosto 2010 16:36:44, Dag Odenhall ha scritto:
> Does this satisfy your needs?
> 
> http://flask.pocoo.org/docs/api/#flask.Flask.add_url_rule

Thank you, just used but too limited.

Davide Muzzarelli

Re: [flask] Werkzeug routing

From:
Armin Ronacher
Date:
2010-08-27 @ 14:24
Hi,

On 2010-08-27 3:35 PM, Davide Muzzarelli wrote:
> For the dispatch_request I have problems because I do not find a way about how
> to replace the call to view_functions instead the Map() call.
I am open for adding a callback there.  But there are multiple places 
where view_function is assumed.  What are the motivations for removing 
the view_functions mapping?


Regards,
Armin

Re: [flask] Werkzeug routing

From:
Davide Muzzarelli
Date:
2010-08-27 @ 14:41
In data venerdì 27 agosto 2010 16:24:08, Armin Ronacher ha scritto:
> On 2010-08-27 3:35 PM, Davide Muzzarelli wrote:
> > For the dispatch_request I have problems because I do not find a way
> > about how to replace the call to view_functions instead the Map() call.
> 
> I am open for adding a callback there.  But there are multiple places
> where view_function is assumed.  What are the motivations for removing
> the view_functions mapping?

I have several packages of modules (like Django) but I need to set the urls 
explicitly for each project because I have to select only the views with the 
behavior that I need. So I centralized the urls in a urls.py file that I 
customize for each project.

I ofthen have to use Submount(). For several projects I have to use 
Subdomain().

The Flask Module is not the right method for me because is too limited for 
what I have to do.

Actually I write this that get the Map from the urls.py file:
app.url_map = import_string(app.config['URLS'])(app)

I build also a class over Flask overriding just this function:
def dispatch_request(self):
        req = _request_ctx_stack.top.request
        try:
            if req.routing_exception is not None:
                raise req.routing_exception
            rule = req.url_rule
            
            if callable(rule.endpoint):
                return rule.endpoint(**req.view_args)
            else:
                if rule.endpoint not in self.view_functions: # cache
                    self.view_functions[rule.endpoint] = 
import_string(rule.endpoint)
                return self.view_functions[rule.endpoint](**req.view_args)
        except HTTPException, e:
            return self.handle_http_exception(e)


I do not know if it is enough or if there are other things that depends on it.

The code have problems if the endpoint is a function:
Traceback (most recent call last):
  File "flask/app.py", line 895, in __call__
    return self.wsgi_app(environ, start_response)
  File "flask/app.py", line 885, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "flask/app.py", line 880, in wsgi_app
    rv = self.preprocess_request()
  File "flask/app.py", line 771, in preprocess_request
    mod = request.module
  File "werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "flask/wrappers.py", line 65, in module
    if self.url_rule and '.' in self.url_rule.endpoint:
TypeError: argument of type 'function' is not iterable

Davide Muzzarelli

Re: [flask] Werkzeug routing

From:
Armin Ronacher
Date:
2010-08-27 @ 16:31
Hi,

On 2010-08-27 4:41 PM, Davide Muzzarelli wrote:
> The Flask Module is not the right method for me because is too limited for
> what I have to do.
That is a well known problem and should be addressed soon.  But we have 
to know exactly how people want to implement pluggable applications so 
we would love to collect usecases.

Maybe you could explain your common application layout and how Flask 
could adapt to that.


Regards,
Armin

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-27 @ 19:06
In data venerdì 27 agosto 2010 18:31:19, Armin Ronacher ha scritto:
> That is a well known problem and should be addressed soon.  But we have
> to know exactly how people want to implement pluggable applications so
> we would love to collect usecases.
> 
> Maybe you could explain your common application layout and how Flask
> could adapt to that.

Ok!
Sorry in advance for my english.


I configure all the application from the settings file. All is explicit so I can 
do special things if I need it, like for larger applications.

With this method I can use a simple approach for a simple application, or a 
complex approach for a big application.


== STRUCTURE ==

My website:
/mysite
    /__init__.py
    /manage.py
    /views.py
    /urls.py
    /models.py
    /templates
        /default.html  # this replace the default.html template of the pages 
module.


If I have a part of an application that I want to reuse, I create a classic 
python package. There is no need to register the package. With this method, 
the reusable module may reside in a package, a single file, in sparse files in 
the filesystem or also in pickled objects too! Very flexible and very easy to 
understand.
    

Example of a reusable package of modules accessible from the standard Python 
path:
/extramodules
    /__init__.py # empty
    /news  # example of a minimal module
        /__init__.py  # contain some view and some models
        /news/news.html
        /news/news_list.html
    /pages  # example of a normal module
        /__init__.py  # empty
        /views.py
        /models.py
        /contexts.py
        /utils.py
        /templates
            /default.html
            /pages/contacts.html
    /blog  # example of a bigger module
        /__init__.py  # may contain a Flask extention
        /views
            /frontend.py
            /admin.py
            /feeds.py
       /models
           /simple.py
           /complex.py
       /contexts.py
       /commands.py
       /utils.py
       /urls.py
       /templates
            /main.html
            /blog/post.html
            /blog/post_list.html
            /news/news_list.html  # this replace the news_list.html template 
of the news module


== SETTINGS ==

The settings can be imported like the actual Flask behavior: from the same 
executable application file or from another file.

In the settings I can load the models that I need:
MODELS = ['news', 'pages.models', 'blog.simple']
...but if I want to use the full power of the blog module:
MODELS = ['news', 'pages.models', 'blog.complex']

In the settings I can also chose what template directory I want to use, I can 
also use an external directory in a custom location:
TEMPLATES = ['/fullpath/mysite/templates', 
'/systempath/python/extramodules/blog/templates', ...]
It is still possible to use a simple trick to set the fullpath using the os 
module, or using an helper function.

URLS = 'mysite.urls.get_url_map' # Import path of urls, pass the "app" 
parameter if it is a callable, so I can use it from a local variable/function 
or an external file

These are really necessary:
STATIC_ROOT = '/fullpath/mysite/static_files'
STATIC_PATH = '/media'

Some template plugins, very easy to add:
TEMPLATE _CONTEXTS = ['extramodules.news.CONTEXTS', 
'extramodules.blog.get_contexts'] # List of variables or callables for the 
template engine.
TEMPLATE_FILTERS = [] # the same thing of template contexts

For the middleware in the same manner:
MIDDLEWARE = ['import.from.string', or.from.object]

The parameters are easy to understand an the developer can pick only what he 
need, in the manner that he want.


== SETTINGS HELPERS - YOUR PLUGGABLE APPLICATIONS METHODS ==

A simple function could create the behavior that we want like Django 
applications, Flask modules and so on.

In a very simple way (also an extention can do it):

from flask.modules import setup_flask_like_modules
URLS, MODELS, TEMPLATES, TEMPLATE_CONTEXTS, MIDDLEWARE = 
setup_flask_like_modules('extramodules.news', 'extramodules.blog')

or

from flask.modules import setup_django_like_modules
URLS, MODELS, TEMPLATES, TEMPLATE_CONTEXTS, MIDDLEWARE = 
setup_django_like_modules('extramodules.news', 'extramodules.blog')


== URLS AND VIEWS ==

Same as Werkzeug, It is very powerful.

It is still possible to create an helper class that return a Map object that 
run like the actual Flask behavior and it is still possible to use the full 
power in order to build a big and complex application.

The endpoint could be a string (it will import the function) or a callable. In 
the second case it is very easy to keep the views inside an object (a thing 
that the Django developers wish since a lot of time! ;)

Example of a complex urls.py file:
from werkzeug.routing import Map, Rule, Subdomain
from extramodules.blog.urls import generic_urls
def get_url_map(app):
    rules = [
	Rule('/', endpoint='mysite.views.index'),
        Subdomain('<string(length=2):lang_code>', generic_urls),
	Rule('/news/', 
endpoint='extramodules.page.views.page_with_seo_extention'),
    ]
    if app.config['SHOW_HIDDEN_PAGE']:
        from mysite import views
        rules.append(Rule('/hidden', endpoint=views.hidden_page))
    return Map(rules)


== FINAL CONSIDERATION ==

With this method it is possible to give a simple and fast method for a 
pluggable application (like Flask modules), or a little more flexible and magic 
one (like Django applications), or also a new method or a custom lighweight 
setup. Explicit is better than implicit, leaving the developer how to organize 
the things.


Davide Muzzarelli

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 14:07
I do a fork of Flask for an example.

http://github.com/Davmuz/flask


== CHANGES ==

- The routing is now managed by the Werkzeug's Map and Rule wrappers, and not 
by the Flask's layer.
- Is it possible to name the urls and pass the view function by string or by 
reference.
- The OPTIONS url parameter is still there, and you can use it also in custom 
Map rules.
- The "route" decorator is still mantained, but in @app.url_map.route().
- The Flask.add_url_rule() method no longer exist because it is possible to 
add a url simply by Flask.url_map.add().
- The helper url_for() still work.
- The internal Flask.view_functions no longer exist because the view functions 
are loaded directly inside the Rules.
- Removed all the Flask's module system, because it will be possible to create 
a custom system.


== COMPATIBILITY ==

I mantained the compatibility as possible, the following are the unique 
changes. The tests runs but not for static files (because not yet finished).

- Replace the decorator @app.route(...) with @app.url_map.route(...).
- Replace the method app.add_url_rule(...) with:
from flask.wrappers import Rule
app.url_map.add(Rule(...))


== HOW TO ADD A VIEW ==

It is possible to use the app.url_map object or also create a new Map() and 
set to it.

By string:
"""
from flask.wrappers import Rule
Rule('/', endpoint='package.module.view')
"""

By reference:
"""
from flask.wrappers import Rule
def index():
    return 'Hello'
Rule('/', endpoint=index)
"""

By named reference:
"""
from flask.wrappers import Rule
def index():
    return 'Hello'
Rule('/', view_func=index, endpoint='hello index')
"""

By decorator:
"""
from flask.wrappers import Rule
@app.url_map.route('/')
def index():
    return 'Hello'
"""


== TODO ==

- Not yet finished with the static url! It will be possible to chose a custom 
directory path where are the static files.
- Documentation not updated, I wait if Armin or others likes the things.


Davide Muzzarelli

Re: [flask] Werkzeug routing (long)

From:
Dag Odenhall
Date:
2010-08-28 @ 15:07
lör 2010-08-28 klockan 16:07 +0200 skrev Davide Muzzarelli:
> I do a fork of Flask for an example.
> 
> http://github.com/Davmuz/flask
> 
> 
> == CHANGES ==
> 
> - The routing is now managed by the Werkzeug's Map and Rule wrappers, and not 
> by the Flask's layer.
> - Is it possible to name the urls and pass the view function by string or by 
> reference.
> - The OPTIONS url parameter is still there, and you can use it also in custom 
> Map rules.
> - The "route" decorator is still mantained, but in @app.url_map.route().
> - The Flask.add_url_rule() method no longer exist because it is possible to 
> add a url simply by Flask.url_map.add().
> - The helper url_for() still work.
> - The internal Flask.view_functions no longer exist because the view functions 
> are loaded directly inside the Rules.
> - Removed all the Flask's module system, because it will be possible to create 
> a custom system.
> 
> 
> == COMPATIBILITY ==
> 
> I mantained the compatibility as possible, the following are the unique 
> changes. The tests runs but not for static files (because not yet finished).
> 
> - Replace the decorator @app.route(...) with @app.url_map.route(...).
> - Replace the method app.add_url_rule(...) with:
> from flask.wrappers import Rule
> app.url_map.add(Rule(...))
> 
> 
> == HOW TO ADD A VIEW ==
> 
> It is possible to use the app.url_map object or also create a new Map() and 
> set to it.
> 
> By string:
> """
> from flask.wrappers import Rule
> Rule('/', endpoint='package.module.view')
> """
> 
> By reference:
> """
> from flask.wrappers import Rule
> def index():
>     return 'Hello'
> Rule('/', endpoint=index)
> """
> 
> By named reference:
> """
> from flask.wrappers import Rule
> def index():
>     return 'Hello'
> Rule('/', view_func=index, endpoint='hello index')
> """
> 
> By decorator:
> """
> from flask.wrappers import Rule
> @app.url_map.route('/')
> def index():
>     return 'Hello'
> """
> 
> 
> == TODO ==
> 
> - Not yet finished with the static url! It will be possible to chose a custom 
> directory path where are the static files.
> - Documentation not updated, I wait if Armin or others likes the things.
> 
> 
> Davide Muzzarelli

You do realize add_url_rule forwards options to Rule()? You can add
view_functions later, by endpoint. If you don't want view_functions,
maybe you'd be more comfortable in raw Werkzeug without Flask?

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 16:32
Hi Dag,
I'm sorry in advance if I do not understand well.

In data sabato 28 agosto 2010 17:07:17, Dag Odenhall ha scritto:
> You do realize add_url_rule forwards options to Rule()?

For me add_url_rule is now unuseful because it adds the OPTIONS method, now it 
is added by the Rule class automatically. Actually, without this necessity, it 
is only a void wrapper over url_map.add().

Now you can use url_map.add(Rule(...)) instead of add_url_rule(...). It is 
more explicit and, if you want, you can now also add others rules factories 
like Subdomain, Submount and EndpointPrefix.

> You can add view_functions later, by endpoint.

view_functions was useful for url_for and for match the view, but this method 
does not permit to use more complex custom urls, so I removed it. Whis this it 
is impossible to use custom urls.

Now the match is made by the endpoint property, and the view is stored 
directly inside the Rule (view_func property), this is the system that replace 
the view_functions.
I used this Werkzeug snippet:
http://dev.pocoo.org/projects/werkzeug/wiki/UsingNamedRulesWithWerkzeugRouting
The Flask dispatch_request() simply call the view_func of the matched rule.

This also open the possibility of named urls and views passed by string.

> If you don't want view_functions, maybe you'd be more comfortable in raw
> Werkzeug without Flask?

Yes, a lot, because the Werkzeug system can scale for a bigger application or 
pluggables.
This modification is soft because you can still use the old easy Flask system 
(also old Flask modules, but I've not implemented it yet).


Davide Muzzarelli
www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Dag Odenhall
Date:
2010-08-28 @ 17:07
lör 2010-08-28 klockan 18:32 +0200 skrev Davide Muzzarelli:
> Hi Dag,
> I'm sorry in advance if I do not understand well.
> 
> In data sabato 28 agosto 2010 17:07:17, Dag Odenhall ha scritto:
> > You do realize add_url_rule forwards options to Rule()?
> 
> For me add_url_rule is now unuseful because it adds the OPTIONS method, now it 
> is added by the Rule class automatically. Actually, without this necessity, it 
> is only a void wrapper over url_map.add().

I think you can override so it does not add OPTIONS if you don't want
that (why not?)

> 
> Now you can use url_map.add(Rule(...)) instead of add_url_rule(...). It is 
> more explicit and, if you want, you can now also add others rules factories 
> like Subdomain, Submount and EndpointPrefix.

Why the need for Rule() here though? Or do you expect one might want to
add something other than a Rule()?

> 
> > You can add view_functions later, by endpoint.
> 
> view_functions was useful for url_for and for match the view, but this method 
> does not permit to use more complex custom urls, so I removed it. Whis this it 
> is impossible to use custom urls.

I don't get what you mean. You can do any URL with Flask that you can do
with Werkzeug, no?

> 
> Now the match is made by the endpoint property, and the view is stored 
> directly inside the Rule (view_func property), this is the system that replace 
> the view_functions.
> I used this Werkzeug snippet:
> http://dev.pocoo.org/projects/werkzeug/wiki/UsingNamedRulesWithWerkzeugRouting
> The Flask dispatch_request() simply call the view_func of the matched rule.
> 
> This also open the possibility of named urls and views passed by string.
> 
> > If you don't want view_functions, maybe you'd be more comfortable in raw
> > Werkzeug without Flask?
> 
> Yes, a lot, because the Werkzeug system can scale for a bigger application or 
> pluggables.
> This modification is soft because you can still use the old easy Flask system 
> (also old Flask modules, but I've not implemented it yet).
> 
> 
> Davide Muzzarelli
> www.dav-muz.net


Re: [flask] Werkzeug routing

From:
Davide Muzzarelli
Date:
2010-08-28 @ 18:05
In data sabato 28 agosto 2010 19:07:29, Dag Odenhall ha scritto:
> I think you can override so it does not add OPTIONS if you don't want
> that (why not?)

Yes, in this case you can ovverride the flask.wrappers.Rule class.

But I like OPTIONS and I keep it in the flask.wrappers.Rule class, so you can 
reuse it for custom routers (see the flollowing question).

There is more: I added url names and views loading from string.

> Why the need for Rule() here though? Or do you expect one might want to
> add something other than a Rule()?

Because it is the unique way to use Subdomain, Submount, EndpointPrefix and 
custom RuleFactories.
It is also possible to create different module systems than the Flask's one.

In my specific case, with this change I can use pluggables, make bigger 
applications in their different ways and customize every application with less 
code reusing my packages.
For a simple example: I have a News plugin with models, views and standard 
urls. So I can use the standard configuration for some websites; for some other 
tailored websites I can configure the urls with different names and use more 
advanced versions of the views with very few lines of code. I do this often 
for my clients.

The real power is to use a pluggable and customize it in few lines without 
touch it and without hacks! Actually I do this since some year.

> I don't get what you mean. You can do any URL with Flask that you can do
> with Werkzeug, no?

No. Before this, you can't add Werkzeug Rules, Maps or other RulesFactories.

After this change you can still use the old Flask method but also do things in 
more advanced methods wihout hacks.

I've just removed some limits keeping the compatibility with the past as 
possible. You can read one of my previous email (the long one) to see where I 
wish to go, sorry if I was not enough clear.

Tell me if there are more things obscure or if you want some example, I'm 
really open because I need it.


Davide Muzzarelli
www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 14:15
In data sabato 28 agosto 2010 16:07:36, Davide Muzzarelli ha scritto:
> I do a fork of Flask for an example.
> 
> http://github.com/Davmuz/flask

Now the flask_tests runs with the static files, all the tests pass.

I'm waiting for comments.


Davide Muzzarelli
www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 17:10
In data sabato 28 agosto 2010 16:15:46, Davide Muzzarelli ha scritto:
> In data sabato 28 agosto 2010 16:07:36, Davide Muzzarelli ha scritto:
> > I do a fork of Flask for an example.
> > 
> > http://github.com/Davmuz/flask

Now it is possible to change the path to the static directory.
You can pass a relative or an absolute path.

Relative path:
"""
app = Flask(__name__)
app.static_root = 'media'
"""

Absolute path:
"""
app = Flask(__name__)
app.static_root = '/home/user/www/media'
"""

This change is compatible with the past and the current flask_tests. 

Davide Muzzarelli
www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Armin Ronacher
Date:
2010-08-28 @ 17:11
Hi,

On 2010-08-28 7:10 PM, Davide Muzzarelli wrote:
> Now it is possible to change the path to the static directory.
> You can pass a relative or an absolute path.
I'm quite busy now and can't check that, but make sure to work against 
the new-modules branch which already changes behaviour there a bit.


Regards,
Armin

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 18:14
In data sabato 28 agosto 2010 19:11:54, Armin Ronacher ha scritto:
> On 2010-08-28 7:10 PM, Davide Muzzarelli wrote:
> > Now it is possible to change the path to the static directory.
> > You can pass a relative or an absolute path.
> 
> I'm quite busy now and can't check that, but make sure to work against
> the new-modules branch which already changes behaviour there a bit.

The change is 26d5cde001fc0948c67f
You can cherry pick directly.

I don't found any documentation about new-modules, what change for the end 
developer?

Davide Muzzarelli
www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Armin Ronacher
Date:
2010-08-28 @ 18:50
Hi,

The new module brach breaks a few things (actually it deprecates) for more
explicit file handling. Currently the static folders are picked up 
automatically when a folder named static exists, in that branch this no 
longer happens, you have to opt-in static folders for a module.

This change fixes issues with google appengine and multiple modules from 
the same folder.

Because it really changes a lot internally from master I would like you to
review if that applies flin new-modules. I can't review that myself 
currently due to the lack of a machine with my dev tools.

Regards,
Armin

(sent from a handheld device)

On 28.08.2010, at 20:14, Davide Muzzarelli <d.muzzarelli@dav-muz.net> wrote:

> In data sabato 28 agosto 2010 19:11:54, Armin Ronacher ha scritto:
>> On 2010-08-28 7:10 PM, Davide Muzzarelli wrote:
>>> Now it is possible to change the path to the static directory.
>>> You can pass a relative or an absolute path.
>> 
>> I'm quite busy now and can't check that, but make sure to work against
>> the new-modules branch which already changes behaviour there a bit.
> 
> The change is 26d5cde001fc0948c67f
> You can cherry pick directly.
> 
> I don't found any documentation about new-modules, what change for the end 
> developer?
> 
> Davide Muzzarelli
> www.dav-muz.net

Re: [flask] Werkzeug routing (long)

From:
Davide Muzzarelli
Date:
2010-08-28 @ 18:57
In data sabato 28 agosto 2010 20:50:20, Armin Ronacher ha scritto:
> The new module brach breaks a few things (actually it deprecates) for more
> explicit file handling. Currently the static folders are picked up
> automatically when a folder named static exists, in that branch this no
> longer happens, you have to opt-in static folders for a module.
> 
> This change fixes issues with google appengine and multiple modules from
> the same folder.
> 
> Because it really changes a lot internally from master I would like you to
> review if that applies flin new-modules. I can't review that myself
> currently due to the lack of a machine with my dev tools.

Thank you Armin, sure I will give some attention.

Davide Muzzarelli
www.dav-muz.net