From alice at gothcandy.com Wed Jul 6 20:33:34 2011 From: alice at gothcandy.com (=?utf-8?Q?Alice_Bevan=E2=80=93McGregor?=) Date: Wed, 6 Jul 2011 14:33:34 -0400 Subject: [Web-SIG] OT: dotted names References: <20110415223315.8FA3B3A4080@sparrow.telecommunity.com> Message-ID: On 2011-04-15 22:33:08 +0000, P.J. Eby said: > At 04:11 PM 4/15/2011 -0400, Fred Drake wrote: >> These end users don't really care if the object identified is a class or >> function in module, a nested attribute on a class, or anything else, so >> long as it does what it's advertised to do. By not pushing implementation >> details into the identifier, the package maintainer is free to change the >> implementation in more ways, without creating backward incompatibility. > > That would be one advantage of using entry points > instead. ;-) (i.e., the user doesn't specify the object location, > the package author does.) > > Note, however, that one must perform considerably more work to > resolve a name, when you don't know whether each part of the name is > a module or an attribute. Not if, as you mention, you use an explicit format. The format my resolver code uses (and this code is utilized in marrow.mailer for manager/transport lookup, marrow.server.http's command-line script to resolve WSGI applications, and marrow.templating to resolve templates) covers the following: :: :: entrypoint_name :: ../relative/path/to/something :: ./relative/path/to/something :: /absolute/path/to/something :: package.relative/path/to/something :: package.absolute.path :: package.submodule:object :: package.submodule:object.attribute What is allowed on any given resolution depends on if the resolver request is looking for an on-disk path or object. Using the above as an example, you can define the use of the SMTP transport within marrow.mailer in two ways: from marrow.mailer.transport.smtp import SMTPTransport config = dict(transport=SMTPTransport) # direct reference config = dict(transport="smtp") # entry point config = dict( # object lookup transport = "marrow.mailer.transport.smtp:SMTPTransport" ) When configuring m.s.http to load an app, you can: # p-code HTTPServer.serve("project.application:WSGIApp.factory") When choosing templates, OTOH, you can do the following: return "./templates/foo.html", dict() return "/var/www/foo.html", dict() return "myapp.templates.foo", dict() return "myapp/templates/foo.html", dict() return "myapp.stemplates:email.welcome", dict() > Either you have to get an AttributeError first, and then fall back to > importing, or get an ImportError first, and fall back to getattr. If you examine the above closely, the differing formats are easily identifiable using a few == and 'in' conditionals: if not isinstance(ref, basestring): return ref if ref[0] == '.': pass # relative if ref[0] == '/': pass # absolute if '/' not in ref and '.' not in ref and ':' not in ref: pass # entrypoint if ':' in ref: import_, _, attrs = ref.partition(':') base = __import__(import_) for attr in attrs.split('.'): base = getattr(base, attr) return attr if '/' in ref: import_, _, path = ref.partition('/') pass # use pkg_resources + path to pull file from package > If the syntax is explicit, OTOH, then you don't have to guess, thereby > saving lots of work and wasteful exceptions. :) ? Alice.