[TransWarp] Resource directories, file extensions, etc.

Phillip J. Eby pje at telecommunity.com
Thu Jul 31 18:25:26 EDT 2003

For resource directories to work, there needs to be a way to specify what 
permissions and factories apply to what files, both on an individual and 
wildcard basis.

I'm considering a couple of different possibilities for how to do 
this.  First, we could use .ini files, and Ty and I previously figured on 
using a 'resource.ini'.  The main downside to this approach is that only a 
limited set of characters and syntax are supported by property names, so 
certain perfectly legal filenames such as '.foo' will not be supportable by 
.ini file rules.  We could treat '.foo' as being identical to 'foo', which 
isn't perfect, but I suppose it might do.  Specifically, we could look up 
properties for filenames as follows:

parts = filter(None, filename.split('.'))  # removes empty parts
for i in range(len(parts)):
     yield '.'.join(parts[i:])

IOW, we look at all "suffixes" of a filename, longest to 
shortest.  'foo.bar' will match 'foo.bar' and 'bar', 'foo.tar.gz' will 
match 'foo.tar.gz', 'tar.gz', and 'gz'.

We'd also need to clean this up a bit to translate any characters not valid 
in property names (e.g. ' ') to '_' or something.  All in all, this 
approach would work, but it doesn't seem especially intuitive as a syntax, 

tar.gz = web.Whatever
gif = web.Something
resource.ini = web.somethingElse

tar.gz = security.Whatever
gif = security.Something
resource.ini = security.somethingElse

Or maybe I'm being too picky.  The main annoyances are the headers and the 
repetition.  I suppose if the file were limited to peak.web.resource 
properties, it could read:

tar.gz = web.Whatever
gif = web.Something
resource.ini = web.somethingElse

tar.gz = security.Whatever
gif = security.Something
resource.ini = security.somethingElse

And that doesn't look quite as bad.  More of an annoyance is the need to 
define a line for each file type; if I want a rule to match .py, .pyc, 
.pyo, .pyd, and .so files, I'll need a line for each, in each section.  If 
it's a trivial rule, no big deal, but if it involves a fair amount of 
computation, it's going to be really annoying.

So my next thought was ZConfig.  It would let me do something like:

FileTypes py pyc pyo pyd so
Permission security.Nobody
Factory peak.web.resources.ForbiddenResource

On the upside, this is a lot easier to read, and a <Files> block could 
allow an unlimited number of match regexes, globs, or individual filenames, 
to which the rules would apply.  On the downside, it would be limited to 
expressing data according to a fixed schema; there would be no way for you 
to redefine arbitrary properties.  Also, questions of precedence ordering 
arise.  Do more specific rules take precedence over more general 
ones?  Rules defined earlier?  Later?  Ugh.

Okay, I think I just convinced myself that ZConfig isn't the way to go for 
this, at least at the present time.  The precedence for ini's is pretty 
clear: most specific to least specific, nearest directory to furthest 
directory.  That is, if a directory declares 'foo.bar' to have a particular 
permission, it's shared by all its subdirectories that don't override 
'foo.bar'.  Defining extension 'bar' in the subdirectory doesn't override 
the parent's foo.bar.

Argh.  While that's *clear*, it's not really what we *want*.  To get what 
we want, we'd have to *reverse* the filenames, e.g., to refer to 'spam.gif' 
we'd need to say:

gif.* = rule for gif's in general
gif.spam = rule for spam.gif

Blech.  This does what we want, but it's nonintuitive and ugly.  (On the 
bright side, it requires only one property lookup: just reverse the 
filename and off you go!)

It almost seems like we want a kind of .ini section where you can say:

[files: *.pyc *.pyo *.py]
peak.web.resource.permissions = [security.Nobody]
peak.web.resource.factories = "peak.web.resources.ForbiddenResource"

and have it translate into the equivalent of:

peak.web.resource.permissions.pyc.* = [security.Nobody]
peak.web.resource.permissions.pyo.* = [security.Nobody]
peak.web.resource.permissions.py.* = [security.Nobody]
peak.web.resource.factories.pyc.* = "peak.web.resources.ForbiddenResource"
peak.web.resource.factories.pyo.* = "peak.web.resources.ForbiddenResource"
peak.web.resource.factories.py.* = "peak.web.resources.ForbiddenResource"

Currently, the .ini file parser has fixed, built-in mechanisms for parsing 
special section headers (e.g. "Load Settings From" and "Provide 
Utilities").  If this were extensible, it'd be easy to add our specialized 
rule mechanism for resources.  In fact, we could generalize this to make 
the existing "load settings from", "load on demand", etc. sections work the 
same way.

I think what I'll do is:

* if a [] section title has a space in it, lowercase it and convert it to a 
property name, converting ' ' to '.'

* Look up the resulting name in 'peak.config.iniFile.sectionParsers', and 
use the found object to parse the section.

* and to support both this and conversion of filenames to property names, 
I'll either add a 'force' keyword to the PropertyName constructor, or add a 
class method that automatically converts invalid characters to '_' and the 
like.  This should also further fix the SQL typeMap issue with Oracle that 
Ty ran into the other day.

The net result would then be that we could define filename rules using a 
sensible syntax, producing intuitive results (i.e. *.gif in subdirectory 
overrides foo.gif in parent directory).  The syntax would not allow 
trailing or embedded wildcards (i.e. no foo.*), but these shouldn't really 
be necessary.  And, the syntax extension will only be usable in resource 
directory .ini files, not just any .ini file.

Okay, I think I'll try it.  :)

There was another issue about file extensions I wanted to bring up, but 
it's completely unrelated to any of the above, except that it has to do 
with file extensions.  :)  But I'll save that for a later post.

More information about the PEAK mailing list