Major changes will be
* return instead of print
* moving away from globals
* better db api
Apart from these, the existing subversion repository is be migrated
to bazaar.
From now on, the official repository will be
http://webpy.org/bzr/webpy.dev
Please let me know if you have any suggestions, objections or feature
requests.
Some of these changes(application stuff and return instead of print)
are already checked in, I request you to play with it and let me know
if there are any bugs.
Here is how the new API is going to look like.
## Hello world
`print` will be replaced with `return`.
import web
urls = (
'/(.*)', 'hello'
)
# more about this later
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name: name = 'world'
return 'Hello,' + name + '!'
if __name__ == "__main__":
app.run()
## database
db configuration will not be global any more. Multiple databases can
be used at the same time and no `web.load()` magic required to make
database work.
import web
db = web.database(dbn='postgres', db='todo', user='you', pw='')
db.select('todo')
db.select('todo', where='id=$id', vars={'id': 2})
db.query('SELECT * FROM todo')
## application
Application is a new way of mapping urls to classes, coming in 0.3.
There will be many different kinds of supported applications.
### web.application
Application to delegate requests based on path.
urls = (
"/hello", "hello",
"/magic/.*", "magic")
app = web.application(urls, globals())
### web.auto_application
Application similar to web.application but urls are constructed
automatiacally using metaclasses.
app = web.auto_application()
class hello(app.page):
def GET(self):
return "hello, world!"
### web.subdir_application
Application to delegate requests based on subdir.
This allows reuse of code easily by taking some exiting app and
mounting it at a directory.
import wiki
import blog
import auth
mapping = (
"/wiki", wiki.app,
"/blog", blog.app,
"/auth", auth.app)
app = web.subdir_application(mapping)
### web.subdomain_application
Application to delegate requests based on host.
This makes virtual hosting very easy.
import mainsite
import usersite
mapping = (
"(www\.)?example.com", mainsite.app,
".*\.example.com", usersite.app
)
app = web.subdomain_application(mapping)
## testing
Testing becomes very easy with applications. Both doctest and
unittest can be used to test web applications.
doctest:
urls = ("/hello", "hello")
app = web.application(urls, globals())
class hello:
"""Hello world example.
>>> response = app.request("/hello")
>>> response.data
'hello, world!'
>>> response.status
'200 OK'
>>> response.headers['Content-Type']
'text/plain'
"""
def GET(self):
web.header('Content-Type', 'text/plain')
return "hello, world!"
unittest:
import unittest
from helloworld import app
class HelloWorldTest(unittest.TestCase):
def testHelloWorld(self):
response = app.request('GET', '/')
self.assertEquals(response.data, 'hello, world!')
self.assertEquals(response.headers['Content-Type'],
'text/plain')
self.assertEquals(response.status, '200 OK')
if __name__ == "__main__":
unittest.main()
## templates
* no whitespace magic
* better error reporting
* should allow template reuse
* Probably use Adam Atlas's implementation
## Contrib
* New module, `web.contrib` with contributed utilities, which are not
part of the web.py core. For example, good auth module (port from
django?) and OpenID support.
Application objects seem a great idea. I think I have seen too many
ad-hoc implementations already.
I don't think the given examples state this, so -- could we also
introduce a response object that could be explicitly used within the
handler methods? For example:
class hello:
def GET(self):
return web.response()
class again:
def POST(self):
return hello.GET(self)
>
> Nice work. What is the planned release date?
That depends on the feedback and feature requests.
> I don't think the given examples state this, so -- could we also
> introduce a response object that could be explicitly used within the
> handler methods?
I don't see any additional additional advantage by doing so?
awesome.
> db = web.database(dbn='postgres', db='todo', user='you', pw='')
awesome.
> ## application
> Application is a new way of mapping urls to classes, coming in 0.3.
> There will be many different kinds of supported applications.
>
> ### web.application
> Application to delegate requests based on path.
awesome.
> ### web.auto_application
> Application similar to web.application but urls are constructed
> automatiacally using metaclasses.
awesome.
> mapping = (
> "/wiki", wiki.app,
> "/blog", blog.app,
> "/auth", auth.app)
>
> app = web.subdir_application(mapping)
SUPER awesome.
> Testing becomes very easy with applications. Both doctest and
> unittest can be used to test web applications.
>
> doctest:
Do I need to say it? Awesome.
> ## templates
I don't use web.template, but I might have to reconsider that now.
> * New module, `web.contrib` with contributed utilities, which are not
> part of the web.py core. For example, good auth module (port from
> django?) and OpenID support.
This is all really great stuff. Stays within the web.py model
of doing things, but makes things much easier to use. Seriously,
I'm excited about this release.
--
David Terrell
d...@meat.net
((meatspace)) http://meat.net/
Why'd you change this to .data from .output?
I just though response data/response body sounds better then response
output.
I don't mind changing that to output, if you wish.
No, I like data; just curious.
So, I am not exactly familiar with Adam Atlas's implementation, but does
it allow templates to import ("include") other templates?
Aside from that, hurray! Especially the new URL handling looks promising.
b^4
What does that mean?
Just removing the four-space indentation style restriction and any
other whitespace sensitivity where there shouldn't be?
It's not "inclusion" in the style of PHP (where it's treated as
though the included template was placed inline in the containing
template), but it does make the `render` object an automatic global
for templates, so you can use that to easily call other templates.
It currently also supports a sort of inheritance by way of $return. I
think Aaron doesn't like that; I'm still open to other ideas. I was
thinking of adding support for decorators ($@... at the top of the
template, before any $def line), and including an `inherit` decorator
which would also be an automatic global, and would look something
like this:
def inherit(parent, *args, **kwargs):
def dec(func):
def _f(*_args, **kwargs):
return parent(func(*_args, **kwargs), *args, **kwargs)
return _f
return dec
For example, if you have these templates:
#base.html
$def with (body, title)
<title>$title</title>
$:body
#index.html
Hello
Then adding $@inherit(render.base, 'Hi') to the top of index.html
would make it equivalent to calling render.base(render.body(), 'Hi').
(Also equivalent would be adding `$return render.base(self, 'Hi')` to
the bottom.) I suppose we should narrow this down (or at least
standardize on one preferred style) on the principle of "There should
be one -- and preferably only one -- obvious way to do it."
The new way to handle this is:
def rendersite(handle):
page = handle()
return render.site(page)
app.add_processor(rendersite)
This passes output of every request through site template.
Adam Atlas, is your implementation available online? Can you share
the url?
What if you don't want every request to go through the template? (I
can think of many, many instances where that would be the case. AJAX
handlers, API type things, dynamically generated images, and pretty
much anything else that's non-HTML.) And what if you want to pass
some argument to the base template (a page title for instance)?
I've always found it much more natural to handle inheritance on a per-
template basis.
> Adam Atlas, is your implementation available online? Can you share
> the url?
I can't remember where I uploaded the last release. But the svn is
<http://svn.adamatlas.org/TTwain>.
-Dougal
web.request.headers
web.request.body
web.response.headers
web.response.body
within the implementation might be a nice touch.
YMMV.
I just did a checkout of the new code and it seems web.loadhocks
disappear?
web.loadhooks['sessionc'] = connect_storm
AttributeError: 'module' object has no attribute 'loadhooks'
We are moving away from using globals.
new way to do that is like this:
app.add_processor(web.loadhook(connect_storm))
I found already out how to use loadhook now, the new Code is really
good documented - Docstrings everywhere (only still miss it for
forms), great!
My Website still don't run, it seems it isn't Unicode aware?
mod_wsgi (pid=7118): Exception occurred within WSGI script '/home/www/
wpy/main.wsgi'.
[Sat Sep 22 17:46:41 2007] [error] [client 127.0.0.1] Traceback (most
recent call last):
[Sat Sep 22 17:46:41 2007] [error] [client 127.0.0.1] File "/usr/lib/
python2.5/site-packages/web/application.py", line 131, in wsgi
[Sat Sep 22 17:46:41 2007] [error] [client 127.0.0.1] result =
[str(result)]
[Sat Sep 22 17:46:41 2007] [error] [client 127.0.0.1]
UnicodeEncodeError: 'ascii' codec can't encode character u'\\u2192' in
position 203: ordinal not in range(128)
Yes it was a bug. Fixed now.
Also to make database work, the following processor must be added to
the application.
def loaddb():
web.db.connect(**web.config.db_parameters)
app.add_processor(web.loadhook(loaddb))
I miss one point on the Roadmap - the date when it may be ready :)
Regarding the docs, it would be great if the docs on the Website
are managed by Version (just like on the Django Website).
That would allow everyone to prepare the docs for upcoming releases.
> Great, its works like a charm now.
> Only Sessions don't work, they aren't port to the new codebase, right?
Not yet. Session code depends on loadhooks, which are now replaced
with app processors.
> I miss one point on the Roadmap - the date when it may be ready :)
I think, the database and application stuff may be ready with in
couple of weeks.
But I am not sure how much the other things.
> Regarding the docs, it would be great if the docs on the Website
> are managed by Version (just like on the Django Website).
> That would allow everyone to prepare the docs for upcoming releases.
Thats a good point.
How about having docs at docs/$version?
I vote aye.
May that behaviour can be customized upon function call:
web.select(view_name, where = x, order = y, autoDispose=False)
{
PS
current.web.select() == new.db.select()
}
Yet another suggestion,
Today's web.select() returns itterBetter object which behave as
forward-only. This behaviour has advantages and disadvantages (e.g.
accessing the database more than once for the same record-set such as
calling len(list(we.select(...))) for a counter).
May that behaviour can be customized upon function call:
web.select(view_name, where = x, order = y, autoDispose=False)
I guess my example was bad.
Here is a better one: (real world) case of a result set, which contain
262144 records.
In that case what I do is caching the list(select), and adding
pagination mechanism at client side.
While web.select takes no time,
Calling list(select(...)) means running over all the records just for
casting itterBetter to list.
On new powerful laptop just list(select)) takes about 10 seconds!!
Whereas end user may only use first 2 or 3 pages.
Hey,
Perhaps I am a little late with this, perhaps it has already been
mentioned (I could not find it on the list right away, but OK):
something just crossed my mind regarding those returns. Will there still
be yield? That is pretty useful for large HTML pages especially if it is
a collection of smaller parts that each take quite some time to
generate. Anyway, I was just wondering about it.
Greetings,
b^4
@Tom + Anand
here is my version:
added self.store[] so instead of raising IndexError, it returns
self.store[i]
# ItterBetter with reverse capabilities
class IterBetter2:
def __init__(self, iterator):
self.i, self.c = iterator, 0
self.store = []
def __iter__(self):
while 1:
x = self.i.next()
self.store.append(x)
yield x
self.c += 1
def __getitem__(self, i):
#todo: slices
if i < self.c:
return self.store[i]
try:
while i > self.c:
x = self.i.next()
self.c += 1
# if this item not in store, add it
if self.c > len(self.store):
self.store.append(x)
# now self.c == i
self.c += 1
self.store.append(self.i.next())
return self.store[i]
except StopIteration:
raise IndexError, str(i)
if __name__ == '__main__':
itr = IterBetter2(iter([x for x in range(12)]))
print itr[3], itr[2], itr[1], itr[0], itr.store
for i in itr:
print i
print itr.store
Do you think reading that much data into memory ist good practise.
I don't know which database your are using, but when I need paging,
I'm using OFFSET and LIMIT for the selects. The user can choose how
much rows per page and each request for the next page ist doing like:
web.select('the_table', offset=25, limit=25)
In this case the user has choosen to get 25 rows per page and has
requested the 2'nd page.
Of course you have to pass the offset along with the HTTP-Requests.
And what about quoting of SQL queries and results? Will it be added?
What about some kind of page controller's arguments processor?
For example something like that:
from myproj.models import ArticleModel
class Home:
@process_arguments (Error404OnFail (ArticleModel), UseDefaultOnFail
(int, 0), str, int)
def GET (s, article, page_number, foo, bar):
...
ArticleModel constructor:
def __init__ (s, row_id=None)
Doing so we initially have in GET method of Home class instance:
article = ArticleModel object that keeps row that was finded by it's
id that was specifed in url
page_number = some integer that was specifed in url or 0 if
transformation of url segment to int raised exception
and so on
Sorry if this is a lame idea, i'm newbie in python programming