We now use getattr to walk the object path, he sugests using item access as well. So that /foo/42 is mapped to
cpg.root.foo[42] or cpg.root.foo.42, depending on whatever is found first.
I've given a recipe on the ticket to show what (s)he can do about it, to resolve this issue using some object
based on the static parameters list that can be filled when root.foo is called with /foo/bar/34/baz/34/...
Do we want this kind of access in the core, or should it be a user sollution to a user specific problem?
On Tue, 04 Jan 2005 09:16:24 +0100, Remco Boerma <remco.boe...@gmail.com> wrote:
> Hi all,
> eurleif asks in #65 if it is possible to map parameters like
> /foo/42/bar/43/baz using another scheme as well.
> We now use getattr to walk the object path, he sugests using item access as
> well. So that /foo/42 is mapped to > cpg.root.foo[42] or cpg.root.foo.42, depending on whatever is found first.
> I've given a recipe on the ticket to show what (s)he can do about it, to
> resolve this issue using some object > based on the static parameters list that can be filled when root.foo is
> called with /foo/bar/34/baz/34/...
> Do we want this kind of access in the core, or should it be a user
> sollution to a user specific problem?
It's an interesting generalization. getitem checking also works on
dicts, so /foo/anything could be mapped to cpg.root.foo[anything]
(where foo is a dict). We have to keep in mind that this may also
cause some undesirable side effects as far as the current lookup
works; if we can be sure that no such side effect exists, I'm +1 on
it. If there's any side effect, -1.
(on the other hand, it can be argued that the user himself can convert
the objects published to support getattr; for example, nothing stops a
UserList or UserDict to support getattr with similar results to the
ones achieved by getitem with similar arguments.)
This ticket also raises an interesting discussion: today it's
difficult for a user to override the existing object mapping function.
The current function is inside a module, and the only way to override
it is to do some black magic and rebind the symbol inside the module
to a new function. I guess we could make it a little bit easier to
register such functions. One possibility is to bind such utility
symbols directly to the cpg structure, at its top level; in this way
it would be easier for the user to customize or override it.
Example:
cpg.mapPathToObject = myObjMapperFunction
Please not that I'm not talking about making the mapPathToObject()
function into a "special" function as filters; there should be only
one such function, not one per published object. What I'm proposing is
just to bind the symbol to the cpg structure so it can be easily bound
to another function byt the site programmer.
Carlos Ribeiro wrote:
>It's an interesting generalization. getitem checking also works on
>dicts, so /foo/anything could be mapped to cpg.root.foo[anything]
>(where foo is a dict). We have to keep in mind that this may also
>cause some undesirable side effects as far as the current lookup
>works; if we can be sure that no such side effect exists, I'm +1 on
>it. If there's any side effect, -1.
Here is some strange behaviour:
>>> d = {}
>>> d.exposed = True
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'dict' object has no attribute 'exposed'
>>> print getattr(d,'exposed')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'dict' object has no attribute 'exposed'
>>>
So checking if there is an exposed, would raise an error itself! (as it should provide None as default)
So we'll have to use other means of checking, or we can't use dictionaries right away. and keeping them all opened, doesn't seem safe to me.
>(on the other hand, it can be argued that the user himself can convert
>the objects published to support getattr; for example, nothing stops a
>UserList or UserDict to support getattr with similar results to the
>ones achieved by getitem with similar arguments.)
Like i did in the ticket. With full control. We might write a recipe about it, but i don't think it's something
to handle in the core. .
>This ticket also raises an interesting discussion: today it's
>difficult for a user to override the existing object mapping function.
>The current function is inside a module, and the only way to override
>it is to do some black magic and rebind the symbol inside the module
>to a new function. I guess we could make it a little bit easier to
>register such functions. One possibility is to bind such utility
>symbols directly to the cpg structure, at its top level; in this way
>it would be easier for the user to customize or override it.
Do we need to support that? If a user wants this to be changed, why can't he/she change the core code?
It's a python module!! I don't think we need to , unless the need really arises . .
>cpg.mapPathToObject = myObjMapperFunction
Well, i don't see the need that much, but if we will change the structure for calling, i would be favouring this. . .
But i simply question how many people will be changing this.. Most of it can be done using regular cp2 'pages'.
The ticket gave me another question:
This one bit me not long ago. .i'm trying to reconstruct the problem while i'm at it. So if you don't have much time:
skip the rest of the email.
/Summary: it shows some odd behaviour using index, default and regular methods.../
<flames state=off>
<start of large post about parameter usage. . if anyone can use this for documentation purposes; go ahead>
What should, and will hapen using the following:
class Root(object);
def index(self,*p):
if p:
print `p`
else:
print 'index!'
index.exposed = True
cpg.root = Root()
Traceback (most recent call last):
File "C:\module\cp2\cherrypy\_cphttptools.py", line 206, in doRequest
handleRequest(wfile)
File "C:\module\cp2\cherrypy\_cphttptools.py", line 372, in handleRequest
func, objectPathList, virtualPathList = mapPathToObject()
File "C:\module\cp2\cherrypy\_cphttptools.py", line 504, in mapPathToObject
raise cperror.NotFound # We didn't find anything
NotFound
So that doesn't work?!
now i add root.bla similar to index. .
def bla(self,*p):
if p:
return `p`
else:
return 'bla'
bla.exposed = True
Okay. here goes: http://localhost/bla and http://localhost/bla/ work fine. . it displays "bla". .
and going to /bla/pir shows ('pir',) .. perfect!! _So something is wrong with index?_. .
Now.
If if would change the bla, to expect 1 parameter, wich i would like to be defaulted, it's becoming a keyword parameter.
>>> bla()
>>> def bla(first='first',*p):
... print first,p
...
>>> bla('pir')
pir ()
>>>
that's nice. . (though one might not expect this to happen, as it's a keyword parameter, but i like the behaviour)
So here's my new bla:
def bla(self,first='first',*p):
return `first`,`p`
bla.exposed = True
/bla returns ; ("'first'", '()')
/bla/pir/feedz/iz/doe returns ("'pir'", "('feedz', 'iz', 'doe')")
exactly what i want it to. .
Back to index.. .
i'll replace my index method with the following;
def index(self,first='first',*p):
return `first`,`p`
index.exposed = True
Traceback (most recent call last):
File "C:\module\cp2\cherrypy\_cphttptools.py", line 206, in doRequest
handleRequest(wfile)
File "C:\module\cp2\cherrypy\_cphttptools.py", line 372, in handleRequest
func, objectPathList, virtualPathList = mapPathToObject()
File "C:\module\cp2\cherrypy\_cphttptools.py", line 504, in mapPathToObject
raise cperror.NotFound # We didn't find anything
NotFound
So we introduce root.default. . . This clearly solves the problem.
def default(self,first='first',*p):
return 'default:',`first`,`p`
default.exposed = True
This solves nearly all of my problems!! now i will just delete the root.index, cause my default is the catch all (i assume)..
Now, all unkown pages, i except to be cought by the default. . . this would be perfect!! (i was writing a wiki at the time,
so going to /wiki would come up with the wiki's frontpage.. that would be nice. . but. .
no, this doesn't work. . if i go to http://localhost/ and i have no root.index, i'm lost . . It is not caught by the default!!
so _default is not run when there is no root.index and the index would be run if it was there..._!
Traceback (most recent call last):
File "C:\module\cp2\cherrypy\_cphttptools.py", line 206, in doRequest
handleRequest(wfile)
File "C:\module\cp2\cherrypy\_cphttptools.py", line 372, in handleRequest
func, objectPathList, virtualPathList = mapPathToObject()
File "C:\module\cp2\cherrypy\_cphttptools.py", line 504, in mapPathToObject
raise cperror.NotFound # We didn't find anything
NotFound
so now i need to redirect my root.index to root.default. . .
a. - how do i do it simple, if default would use generators (thus return a generator) ? cause
root.index(self): return self.default() would clearly not be a sollution then
b. - _can't default become the catch all, even for missing indexes_? As i thing this behaviour would be more 'natural' and expected.
</start of large post about parameter usage. . if anyone can use this for documentation purposes; go ahead>
</flames>
I hope this can serve both for documentation purposes, as well as for feature request/bugreport . . .
and (okay, flame me for starting this infamous debate again). . .
_if default would become the catch all, even for missing indexes, can't we simply drop default, and have index have the default behaviour as well?
_I think it would be much simpler then. .
> On Tue, 04 Jan 2005 09:16:24 +0100, Remco Boerma
<remco.boe...@gmail.com> wrote:
> > Hi all,
> > eurleif asks in #65 if it is possible to map parameters like
> > /foo/42/bar/43/baz using another scheme as well.
> > We now use getattr to walk the object path, he sugests using item
access as
> > well. So that /foo/42 is mapped to > > cpg.root.foo[42] or cpg.root.foo.42, depending on whatever is found
first.
> > I've given a recipe on the ticket to show what (s)he can do about
it, to
> > resolve this issue using some object > > based on the static parameters list that can be filled when root.foo is
> > called with /foo/bar/34/baz/34/...
> > Do we want this kind of access in the core, or should it be a user
> > sollution to a user specific problem?
> It's an interesting generalization. getitem checking also works on
> dicts, so /foo/anything could be mapped to cpg.root.foo[anything]
> (where foo is a dict). We have to keep in mind that this may also
> cause some undesirable side effects as far as the current lookup
> works; if we can be sure that no such side effect exists, I'm +1 on
> it. If there's any side effect, -1.
> (on the other hand, it can be argued that the user himself can convert
> the objects published to support getattr; for example, nothing stops a
> UserList or UserDict to support getattr with similar results to the
> ones achieved by getitem with similar arguments.)
> This ticket also raises an interesting discussion: today it's
> difficult for a user to override the existing object mapping function.
> The current function is inside a module, and the only way to override
> it is to do some black magic and rebind the symbol inside the module
> to a new function. I guess we could make it a little bit easier to
> register such functions. One possibility is to bind such utility
> symbols directly to the cpg structure, at its top level; in this way
> it would be easier for the user to customize or override it.
> Example:
> cpg.mapPathToObject = myObjMapperFunction
> Please not that I'm not talking about making the mapPathToObject()
> function into a "special" function as filters; there should be only
> one such function, not one per published object. What I'm proposing is
> just to bind the symbol to the cpg structure so it can be easily bound
> to another function byt the site programmer.
> and (okay, flame me for starting this infamous debate again). . .
> _if default would become the catch all, even for missing indexes, can't > we simply drop default, and have index have the default behaviour as well?
> _I think it would be much simpler then. .
Remi insists to keep both and he has some good points for this so maybe
it's time we stop arguing the case :)
>>and (okay, flame me for starting this infamous debate again). . .
>>_if default would become the catch all, even for missing indexes, can't >>we simply drop default, and have index have the default behaviour as well?
>>_I think it would be much simpler then. .
>Remi insists to keep both and he has some good points for this so maybe
>it's time we stop arguing the case :)
Please, continue the docs i started in the mail, and explain how to forward the index to the default if you want that behaviour
without having problems with the generator issue. .
Sylvain Hellegouarch wrote:
>I like the idea but on the other hand is there such a real need ?
Well, actually, i don't like the idea ;)) but the bloke asked. .
I already gave a sollution, but i guess he feels it should be done quite a bit simpler. . and should
be supported by the core. I wrote in the ticket i would do an oppinion poll in here, as it is a feature
request.
On Tue, 04 Jan 2005 10:39:12 +0100, Remco Boerma <remco.boe...@gmail.com> wrote:
> Carlos Ribeiro wrote: > It's an interesting generalization. getitem checking also works on dicts,
> so /foo/anything could be mapped to cpg.root.foo[anything] (where foo is a
> dict). We have to keep in mind that this may also cause some undesirable
> side effects as far as the current lookup works; if we can be sure that no
> such side effect exists, I'm +1 on it. If there's any side effect, -1. Here
> is some strange behaviour:
> >>> d = {}
> >>> d.exposed = True
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> AttributeError: 'dict' object has no attribute 'exposed'
> >>> print getattr(d,'exposed')
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> AttributeError: 'dict' object has no attribute 'exposed'
> So checking if there is an exposed, would raise an error itself! (as it
> should provide None as default)
> So we'll have to use other means of checking, or we can't use dictionaries
> right away. and keeping them all opened, doesn't seem safe to me.
For dicts this is not a problem, because I can simply make:
d[exposed] = True
... and if I use the getitem method as a fallback for getattr, I'll
get the expected result.
> (on the other hand, it can be argued that the user himself can convert the
> objects published to support getattr; for example, nothing stops a UserList
> or UserDict to support getattr with similar results to the ones achieved by
> getitem with similar arguments.) Like i did in the ticket. With full
> control. We might write a recipe about it, but i don't think it's something
> to handle in the core. .
Why not provide an "ExposeItems" class, that can take any
getitem-aware class and publish it? It would be a simple wrapper. A
quick hack (using Wiki markup, mind you ;-):
Of course, this code assumes that the members of mylist and mydict are
itself exposed objects (it only publishes the *list* or *dict*, not
it's members).
<snipped long discussion about index vs default>
> if default would become the catch all, even for missing indexes, can't we
> simply drop default, and have index have the default behaviour as well? > I think it would be much simpler then. .
I'm +1 for it. But that's a difficult debate, indeed.
I like your ExposeItems sollution. it's short and solves all well. i guess.. but i think it would have to include
some form of default handlers/error handles if the page is not found.
>>if default would become the catch all, even for missing indexes, can't we
>>simply drop default, and have index have the default behaviour as well? >>I think it would be much simpler then. .
>I'm +1 for it. But that's a difficult debate, indeed.
I know.. but if default, like it's name, would indeed also catch the http://localhost/ problem, i would be fine with it as well.
So let met drop the infamous debate.
Let's discuss, if the default method should be called, when there is a request that would 'normally' map to the index, but will be
mapped to default _if no index exists_!!
This would allow for the regular default and index usage, but it will also mean default is a catch all sollution.
It would certainly make my life easier ;)
> >I like the idea but on the other hand is there such a real need ?
> Well, actually, i don't like the idea ;)) but the bloke asked. .
> I already gave a sollution, but i guess he feels it should be done quite > a bit simpler. . and should
> be supported by the core. I wrote in the ticket i would do an oppinion > poll in here, as it is a feature
> request.
> We now use getattr to walk the object path, he sugests using item access > as well. So that /foo/42 is mapped to
> cpg.root.foo[42] or cpg.root.foo.42, depending on whatever is found first.
> I've given a recipe on the ticket to show what (s)he can do about it, to > resolve this issue using some object
> based on the static parameters list that can be filled when root.foo is > called with /foo/bar/34/baz/34/...
> Do we want this kind of access in the core, or should it be a user > sollution to a user specific problem?
I don't want this in the core.
I think that having multiple object-mapping methods will only confuse people.
The core should provide *one* clear implementation that suits 95% (if not 99%) of the people.
If some people want to use some other esoteric mapping methods, they can do so by having an InputFilter that sets cpg.request.objectPath based on cpg.request.path (no need to override the default mapping function).
> What should, and will hapen using the following:
> class Root(object);
> def index(self,*p):
> if p:
> print `p`
> else:
> print 'index!'
> index.exposed = True
> cpg.root = Root()
Now this is a bug !! You should get a "NotFound" error in this case
I added a ticket (#70) for this.
> .. perfect!! _So something is > wrong with index?_. .
No, something is wrong with the current implementation of the algorithm.
> and (okay, flame me for starting this infamous debate again). . .
> _if default would become the catch all, even for missing indexes, can't > we simply drop default, and have index have the default behaviour as well?
> _I think it would be much simpler then. .
Please let's not go over this again ...
If you want to use URLs like "http://domain/page/param1/param2" instead of keyword arguments, the way to go is to use "default".
> > We now use getattr to walk the object path, he sugests using item access
> > as well. So that /foo/42 is mapped to
> > cpg.root.foo[42] or cpg.root.foo.42, depending on whatever is found first.
> > I've given a recipe on the ticket to show what (s)he can do about it, to
> > resolve this issue using some object
> > based on the static parameters list that can be filled when root.foo is
> > called with /foo/bar/34/baz/34/...
> > Do we want this kind of access in the core, or should it be a user
> > sollution to a user specific problem?
> I don't want this in the core.
> I think that having multiple object-mapping methods will only confuse
> people.
> The core should provide *one* clear implementation that suits 95% (if
> not 99%) of the people.
> If some people want to use some other esoteric mapping methods, they can
> do so by having an InputFilter that sets cpg.request.objectPath based on
> cpg.request.path (no need to override the default mapping function).
I agree with you... and that's why I proposed (and implemented) a
solution that does not mess up with the core, and neither with
objmapping, and it's also reusable in other contexts. It's on the
library (cherrypy.lib). I hope you don't mind... I assumed that the
library was the place for this type of utility code, and also, I
assumed that it was "better to ask for forgiveness than for
permission". :-)
> Now this is a bug !! You should get a "NotFound" error in this case
> I added a ticket (#70) for this.
No way. it should, it works perfectly. . .i allow every parameter, using the positional scheme. . and it only displays the parameter, which should be 'pir'
and it is!! I'ts perfect!
>> .. perfect!! _So something is wrong with index?_. .
> No, something is wrong with the current implementation of the algorithm.
i don't get it . .
>> and (okay, flame me for starting this infamous debate again). . .
>> _if default would become the catch all, even for missing indexes, >> can't we simply drop default, and have index have the default >> behaviour as well?
>> _I think it would be much simpler then. .
> Please let's not go over this again ...
Like i wrote in another mail. . it's fine with me, i rest my case.. .
> If you want to use URLs like "http://domain/page/param1/param2" > instead of keyword arguments, the way to go is to use "default".
But this will not work if i goto say http://localhost/ and i have no index. . Can't the default catch this as well? If there is an index: use it, if there isn't: try default: if there is no default: raise an error??
Example:
/wiki/index and /wiki should all produce the front wiki page (internal redirect to /wiki/wiki)
/wiki/edit/pagename should edit the page
/wiki/view/pagename or /wiki/pagename should show the page for pagename (i take care of checking if the pagename is 'view' or 'edit' myself .. that's my business. .
where /wiki/wiki is a special case, as that's the one redirected to . .
Now i need to add the logic in both index and default, right?
if i could drop the /wiki/index call, and let /wiki/default handle it all, it would suit me just fine!
>> Now this is a bug !! You should get a "NotFound" error in this case
>> I added a ticket (#70) for this.
> No way. it should, it works perfectly.. .i allow every parameter,
> using the positional scheme. . and it only displays the parameter, which > should be 'pir'
> and it is!! I'ts perfect!
Well, it may be what you want in this specific case, but this creates other problems ...
This decision was made a while ago: Only "default" can be called with positional arguments, when the URL includes additional items in the path
So it *is* a bug and the code will be changed so that you get a "NotFound" error in that case ...
The way to go for what you want to achieve *is* to use "default" ...
Remi Delon wrote:
> Well, it may be what you want in this specific case, but this creates > other problems ...
> This decision was made a while ago: Only "default" can be called with > positional arguments, when the URL includes additional items in the path
> So it *is* a bug and the code will be changed so that you get a > "NotFound" error in that case ...
> The way to go for what you want to achieve *is* to use "default" ...
I'm fine with that, sure i am. . but this also means that if no index was found, default was used? That would suite me fine as well. .
(Btw, we do have to check breaking the positional scheme doesn't brake the entire xmlrpc functionality. you can pass multiple parameters to whatever function you want of course!)
I just configured the SVN repository to run behind Apache as opposed to using svn+ssh.
The new URL is https://svn.cherrypy.org This means that write accessed is now done with a .htpasswd file.
People who had write access before should give me their htpasswd entry so I can add it to the file.
People who don't have write access yet but would like to get involved and contribute code to CherryPy, well, here is your chance :-)
Remi
PS: "htpasswd" is a command line tool that comes with Apache and generates an encrypted version of your password.
An "htpasswd entry" looks like this: rdelon:MEFxQ1jeucFZI
If you don't have htpasswd on your computer, you can generate the line online at this URL: http://www.flash.net/cgi-bin/pw.pl
>> This decision was made a while ago: Only "default" can be called with >> positional arguments, when the URL includes additional items in the path
>> So it *is* a bug and the code will be changed so that you get a >> "NotFound" error in that case ...
Well, I checked in that fix yesterday.
>> The way to go for what you want to achieve *is* to use "default" ...
> I'm fine with that, sure i am. . but this also means that if no index > was found, default was used? That would suite me fine as well. .
Yes, it does.
> (Btw, we do have to check breaking the positional scheme doesn't brake > the entire xmlrpc functionality. you can pass multiple parameters to > whatever function you want of course!)
Remi Delon wrote:
>> I'm fine with that, sure i am. . but this also means that if no index >> was found, default was used? That would suite me fine as well .
Traceback (most recent call last):
File "C:\module\cp2\cherrypy\_cphttptools.py", line 206, in doRequest
handleRequest(wfile)
File "C:\module\cp2\cherrypy\_cphttptools.py", line 372, in handleRequest
func, objectPathList, virtualPathList = mapPathToObject()
File "C:\module\cp2\cherrypy\_cphttptools.py", line 511, in mapPathToObject
raise cperror.NotFound # We didn't find anything
NotFound
Goting to /bla works well, and going to /foo gives me the answer of default (like it should).
But as i have no index, going to root doesn't work well, and does _not_ map to default.
Guess i spotted a bug :)
test 1 and 14 have a direct relationship. . In test 1 it fails, because in the _cphttptools.py on line 485 we test on objectPathList not to be empty. . and on line 494 the following block is never executed, because it is the first call. If it is skipped, it will not test for deafault, and after that the list is empty: so it will raise an error. So that explains 1 and 14
10 and 11 are the same error of course. . but i don't get it. Why is 10 not allowed, when 2,3,4 and 9 are all ok? it mapes to root.default insatead of root.recursive.bla
. . Another bug?
I hope the provided test table gives some support. .
>> (Btw, we do have to check breaking the positional scheme doesn't >> brake the entire xmlrpc functionality. you can pass multiple >> parameters to whatever function you want of course!)
> Well, can you check now ?
Test if the multiple parameters passed are returned well ... ok
Test if positional parameters sever side are no problem ... ok
Test if objects within objects are accepted with positional parameters and parameters given by path. ... ERROR
UNIMPLEMENTED TEST; Test if an invalid request returns with a proper error ... FAIL
SHOULD FAIL; Test if the Generator result is returned as a string ... ERROR
Test if objects within objects are accepted. ... ok
Test if an object returned from the server is a dicationary. ... ok
Test if keyword parameters are not allowed, as they shouldn't be.. ... ok
Test if None is handled properly ... ok
Test if a string is returned succesfully ... ok
Test if two strings are returned succesfully ... ok
UNIMPLEMENTED TEST; Test if the mimetype is set well ... FAIL
Test if the server is running or not and wheter a simple function call is working. ... ok
Test if the failure return as a Fault object is returned and contains the right value. ... ok
It's to bad the third test breaks. . all the other tests seem to go fine. . (they had the same behaviour as before, and i have not implemented all tests as you can see).
The third test is like:
assertEqual(self.server.a.c.param1.param2('param3'),['param1','param2','par am3'])
whitch maps to root.a.c(*p): return p
so now i would have to catch this in root.a.default and map to root.a.c myself, i think it's tolerable. .