Problem
The set of legal Python identifiers is a subset of the set of legal URI path component names. Thus, "/1foo/2bar/foo-bar" is a perfectly legal (part of a) URI, but neither "1foo", nor "2bar", nor "foo-bar" are legal Python identifiers; that is, attempts to define classes, functions, methods, or variables with leading digits or containing dash/hyphen characters (or spaces, for that matter) will result in SyntaxError.
So if you want or need to use these illegal Python names as components of Quixote URIs, what can you do?
Solution
You have a few options:
- provide a mapping between the URL name and the Python name in _q_exports (new to Quixote 1.0)
- use _q_lookup()
- use _q_resolve()
- play around with Python internals
Discussion
Let's make a URI for a Quixote app with an illegal Python name, something like "http://foo/rdf-model".
Providing a mapping in _q_exports
_q_exports = [ ("rdf-model", "rdf_model"), ]
Using _q_lookup
Another solution is to define a _q_lookup():
def rdf_model(request): pass def _q_lookup(request, component): if component == "rdf-model": return rdf_model(request)
That works and is obvious. And Quixote developers suggest that it won't be a performance problem unless your're serving hundreds of requests per second. That being said, it's a bit hackish, since we're not *really* doing a dynamic lookup.
Using _q_resolve
The other solution, especially if this illegally named component isn't really dynamic, is to define a _q_resolve():
def rdf_model(request): pass def _q_resolve(component): if component == "rdf-model": return rdf_model
The difference compared to the _q_lookup solution is that _q_resolve doesn't dynamically lookup the component for each request; but, rather, does it once for all, storing whatever is returned on the object. As Andrew Kuchling said when he described this approach: "The first time _q_resolve is run, the publisher will do setattr(module, 'rdf-model', <return value>), and _q_resolve won't be called again."
Python internals
You can always try to muck about with Python internals directly, though I suspect this approach doesn't offer much advantage over the previous two, and it may just make things blow up in your face.
Changing globals
You can always add the appropriate entry to globals():
def rdf_model(request): pass globals()['rdf-model'] = rdf_model
(Suggested by Neil Schemenauer.)
Changing modules
It's also possible -- this was suggested by Jason Sibre -- to muck about with sys.modules:
def rdf_model(request): pass _q_exports = ["rdf-model"] #put the illegal name in the exports import sys mod = sys.modules[__name__] setattr(mod, "rdf-name", rdf_model)
Though, near as I can tell, this is never to be preferred to the _q_resolve solution.