|
From: Robin M. <rob...@gm...> - 2006-05-22 11:09:09
|
SSd2ZSBiZWVuIGxvb2tpbmcgb3ZlciB0aGUgQ29uZmlnT2JqIGRvY3MsIGFuZCBmb3IgdGhlIG1v c3QgcGFydCwgSQpyZWFsbHkgbGlrZSB3aGF0IEkgc2VlLgoKVGhlcmUncyBvbmUgcGxhY2Ugd2hl cmUgSSB0aGluayBJIHNlZSByb29tIGZvciBpbXByb3ZlbWVudCwgdGhvdWdoOgpzdHJpbmcgaW50 ZXJwb2xhdGlvbi4KClRoZSBtYWluIHJlYXNvbiBmb3IgaGF2aW5nIC5pbmktc3R5bGUgY29uZmln dXJhdGlvbiBmaWxlcywgaW4gbXkKb3BpbmlvbiwgaXMgdG8gYWxsb3cgZW5kIHVzZXJzICh3aG8g YXJlIHVzdWFsbHkgbm9uLXByb2dyYW1tZXJzKSB0bwptb2RpZnkgdGhlIGNvbmZpZ3VyYXRpb24g ZmlsZXMgd2l0aG91dCB0b28gbXVjaCBkaWZmaWN1bHR5LiBCdXQgc2luY2UKSSd2ZSBzdGFydGVk IHdvcmtpbmcgb24gdGVjaC1zdXBwb3J0IGpvYnMsIEkndmUgbGVhcm5lZCB0aGF0IHlvdSBkb24n dAp3YW50IHRvIG1ha2UgZW5kIHVzZXJzIHJlbWVtYmVyIHRvbyBtdWNoIGF0IG9uY2UuIE1vc3Qg cGVvcGxlIHdobwpzdHVkaWVkIGFsZ2VicmEgYXJlIGF0IGxlYXN0IGNvbWZvcnRhYmxlIHdpdGgg dGhlIGlkZWEgb2YgdmFyaWFibGUKc3Vic3RpdHV0aW9uIC0tIGJ1dCB0aGV5IHdvbid0IHJlbWVt YmVyIHRoZSAlKGZvbylzIGZvcm1hdCBuYXR1cmFsbHkuClRoYXQncyBmb3VyIGRpZmZlcmVudCBj aGFyYWN0ZXJzIHRvIHR5cGUgZm9yIGEgc2luZ2xlIHZhcmlhYmxlCnN1YnN0aXR1dGlvbi4gSXQn cyBlYXN5IGZvciB0aG9zZSBvZiB1cyB3aG8gYXJlIHByb2dyYW1tZXJzIGFuZCB3aG9zZQptaW5k cyBhcmUgdHJhaW5lZCB0byB0aGluayBpbiB0ZXJtcyBvZiBtZWFuaW5nZnVsIHB1bmN0dWF0aW9u LCBidXQKbW9zdCBwZW9wbGUncyBtaW5kcyBqdXN0IGRvbid0IHRoaW5rIHRoYXQgd2F5LgoKVGhp cyBpcyBleGFjdGx5IHdoeSBQRVAgMjkyIChodHRwOi8vd3d3LnB5dGhvbi5vcmcvZGV2L3BlcHMv cGVwLTAyOTIvKQp3YXMgd3JpdHRlbi4gSXQgcmVwbGFjZXMgdGhlICIlKGZvbylzIiBzeW50YXgg d2l0aCBhIHNpbXBsZXIgc3ludGF4LAoiJGZvbyIuIChXaXRoIHRoZSBvcHRpb24gb2YgIiR7Zm9v fSIgaWYgZGlzYW1iaWd1YXRpb24gaXMgbmVlZGVkLAplLmcuLCAiJHtub3VufWlmaWNhdGlvbiIp LgoKUEVQIDI5MiBpcyBpbiB0aGUgc3RkbGliIGFzIG9mIFB5dGhvbiAyLjQgKGFzIHRoZSBzdHJp bmcuVGVtcGxhdGUKY2xhc3MpLCBidXQgaXRzIGltcGxlbWVudGF0aW9uIGluIHRoZSBzdHJpbmcg bW9kdWxlIHdvdWxkIGVhc2lseSB3b3JrCnVuZGVyIFB5dGhvbiAyLjIgYW5kIGFib3ZlLiAoVGhl IG9ubHkgcG9zdC0yLjIgZmVhdHVyZXMgaXQgdXNlcyBhcmUKdGhlIG5hbWVzIFRydWUgYW5kIEZh bHNlKS4gVGhlIGltcGxlbWVudGF0aW9uIGluIHRoZSBzdGRsaWIgaXMgYWJvdXQKMTAwIGxpbmVz IG9mIGNvZGUsIHNvIGl0IHNob3VsZG4ndCBiZSB0b28gY29tcGxpY2F0ZWQgdG8gYWRkIHRvCkNv bmZpZ09iai4KClN0cmluZyBpbnRlcnBvbGF0aW9uIGNhbiBiZSBhIGhhbmR5IGZlYXR1cmUuIEkn ZCBsaWtlIHRvIHNlZSBpdCBhcwpzaW1wbGUgYXMgcG9zc2libGUuCgotLSAKUm9iaW4gTXVubgpS b2Jpbi5NdW5uQGdtYWlsLmNvbQpHUEcga2V5IDB4RDY0OTcwMTQK |
|
From: Michael F. <fuz...@vo...> - 2006-05-22 12:46:18
|
Robin Munn wrote: > I've been looking over the ConfigObj docs, and for the most part, I > really like what I see. > > There's one place where I think I see room for improvement, though: > string interpolation. The new syntax looks good (better), and the proposal is reasonable. *However*, the original syntax is compatible with ConfigParser and there are already people using it with ConfigObj. Maintaining two different syntaxes sounds like a nightmare... I can't think of an obvious middle ground. Adding it as an extra option further complicates ConfigObj and I would still have to have the old syntax as the default (making the new syntax less likely to be used.) It could probably be implemented as a separate function that uses ``walk`` to do the substitution. If you get this working I would be interested in seeing it. (And happy to put a reference to it in the docs.) Fuzzyman http://www.voidspace.org.uk/python/shareware.shtml > > The main reason for having .ini-style configuration files, in my > opinion, is to allow end users (who are usually non-programmers) to > modify the configuration files without too much difficulty. But since > I've started working on tech-support jobs, I've learned that you don't > want to make end users remember too much at once. Most people who > studied algebra are at least comfortable with the idea of variable > substitution -- but they won't remember the %(foo)s format naturally. > That's four different characters to type for a single variable > substitution. It's easy for those of us who are programmers and whose > minds are trained to think in terms of meaningful punctuation, but > most people's minds just don't think that way. > > This is exactly why PEP 292 (http://www.python.org/dev/peps/pep-0292/) > was written. It replaces the "%(foo)s" syntax with a simpler syntax, > "$foo". (With the option of "${foo}" if disambiguation is needed, > e.g., "${noun}ification"). > > PEP 292 is in the stdlib as of Python 2.4 (as the string.Template > class), but its implementation in the string module would easily work > under Python 2.2 and above. (The only post-2.2 features it uses are > the names True and False). The implementation in the stdlib is about > 100 lines of code, so it shouldn't be too complicated to add to > ConfigObj. > > String interpolation can be a handy feature. I'd like to see it as > simple as possible. > |
|
From: Robin M. <rob...@gm...> - 2006-05-22 14:04:28
|
T24gNS8yMi8wNiwgTWljaGFlbCBGb29yZCA8ZnV6enltYW5Adm9pZHNwYWNlLm9yZy51az4gd3Jv dGU6Cj4gUm9iaW4gTXVubiB3cm90ZToKPiA+IEkndmUgYmVlbiBsb29raW5nIG92ZXIgdGhlIENv bmZpZ09iaiBkb2NzLCBhbmQgZm9yIHRoZSBtb3N0IHBhcnQsIEkKPiA+IHJlYWxseSBsaWtlIHdo YXQgSSBzZWUuCj4gPgo+ID4gVGhlcmUncyBvbmUgcGxhY2Ugd2hlcmUgSSB0aGluayBJIHNlZSBy b29tIGZvciBpbXByb3ZlbWVudCwgdGhvdWdoOgo+ID4gc3RyaW5nIGludGVycG9sYXRpb24uCj4g VGhlIG5ldyBzeW50YXggbG9va3MgZ29vZCAoYmV0dGVyKSwgYW5kIHRoZSBwcm9wb3NhbCBpcyBy ZWFzb25hYmxlLgo+Cj4gKkhvd2V2ZXIqLCB0aGUgb3JpZ2luYWwgc3ludGF4IGlzIGNvbXBhdGli bGUgd2l0aCBDb25maWdQYXJzZXIgYW5kIHRoZXJlCj4gYXJlIGFscmVhZHkgcGVvcGxlIHVzaW5n IGl0IHdpdGggQ29uZmlnT2JqLgo+Cj4gTWFpbnRhaW5pbmcgdHdvIGRpZmZlcmVudCBzeW50YXhl cyBzb3VuZHMgbGlrZSBhIG5pZ2h0bWFyZS4uLgo+Cj4gSSBjYW4ndCB0aGluayBvZiBhbiBvYnZp b3VzIG1pZGRsZSBncm91bmQuIEFkZGluZyBpdCBhcyBhbiBleHRyYSBvcHRpb24KPiBmdXJ0aGVy IGNvbXBsaWNhdGVzIENvbmZpZ09iaiBhbmQgSSB3b3VsZCBzdGlsbCBoYXZlIHRvIGhhdmUgdGhl IG9sZAo+IHN5bnRheCBhcyB0aGUgZGVmYXVsdCAobWFraW5nIHRoZSBuZXcgc3ludGF4IGxlc3Mg bGlrZWx5IHRvIGJlIHVzZWQuKQoKSG93IGFib3V0IG1ha2luZyB0aGUgImludGVycG9sYXRlIiBh dHRyaWJ1dGUgYWN0IGFzIGEga2luZCBvZgp0aHJlZS13YXkgc3dpdGNoPyAiaW50ZXJwb2xhdGU9 RmFsc2UiIChvciBOb25lIG9yIGFueSBvdGhlciBmYWxzZQp2YWx1ZSkgZm9yIG5vIGludGVycG9s YXRpb24sICJpbnRlcnBvbGF0ZT0nQ29uZmlnUGFyc2VyJyIgZm9yCkNvbmZpZ1BhcnNlci1jb21w YXRpYmxlIGludGVycG9sYXRpb24gKHRoZSBjdXJyZW50IGJlaGF2aW9yKSwgYW5kCiJpbnRlcnBv bGF0ZT0nVGVtcGxhdGUnIiBmb3Igc3RyaW5nLlRlbXBsYXRlLWJhc2VkIGludGVycG9sYXRpb24g KHRoZQpuZXcgYmVoYXZpb3IgSSdtIHN1Z2dlc3RpbmcpLiBGb3IgYmFja3dhcmRzIGNvbXBhdGli aWxpdHksCiJpbnRlcnBvbGF0ZT1UcnVlIiB3b3VsZCBiZWhhdmUgbGlrZSAiaW50ZXJwb2xhdGU9 J0NvbmZpZ1BhcnNlciciLgooVGhpcyB3b3VsZCBhbHNvIGFsbG93IGZvciBhIGdyYWR1YWwgcGhh c2luZyBvdXQgb2YgQ29uZmlnUGFyc2VyLWxpa2UKYmVoYXZpb3IgaWYsIGluIHRoZSBkaXN0YW50 IGZ1dHVyZSwgIlRlbXBsYXRlIiBiZWNvbWVzIHRoZSByZWNvbW1lbmRlZApkZWZhdWx0LiBGaXJz dCBkZXByZWNhdGUgdGhlIG9sZCBiZWhhdmlvciwgdGhlbiBzdGFydCBnZW5lcmF0aW5nCndhcm5p bmdzLCB0aGVuIHN3aXRjaCB3aGF0ICJpbnRlcnBvbGF0ZT1UcnVlIiBtZWFucy4gQWxsIHRoYXQg b3ZlcgpzZXZlcmFsIG1ham9yIHJlbGVhc2VzLCBvZiBjb3Vyc2UpLgoKTWFpbnRhaW5pbmcgdHdv IGRpZmZlcmVudCBpbnRlcnBvbGF0aW9uIHN5bnRheGVzIGRvZXMgc291bmQgbGlrZSBhIGJpdApv ZiBhIGNoYWxsZW5nZSwgSSBhZ3JlZS4gOi0pIEFsbG93aW5nICpib3RoKiB0byBiZSB1c2VkIGlu IHRoZSBzYW1lCmNvbmZpZ3VyYXRpb24gZmlsZSB3b3VsZCBpbmRlZWQgYmUgYSBuaWdodG1hcmU7 IGJ1dCBJIHRoaW5rIHRoZXJlJ3MgYQpjbGVhbiB3YXkgdG8gZG8gaXQuIEUuZy4sOgoKCgpjbGFz cyBJbnRlcnBvbGF0aW9uQmFzZShvYmplY3QpOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHNlY3Rp b24pOgogICAgICAgIHNlbGYuc2VjdGlvbiA9IHNlY3Rpb24gICMgU2VjdGlvbiBvYmplY3QgdGhh dCAib3ducyIgdGhpcyBpbnN0YW5jZQogICAgZGVmIGludGVycG9sYXRlKHNlbGYsIHZhbHVlKToK ICAgICAgICBwYXNzICAgIyBBYnN0cmFjdCBiYXNlIGNsYXNzLCBkZXNjZW5kYW50cyBwcm92aWRl IGltcGxlbWVudGF0aW9ucwoKY2xhc3MgQ29uZmlnUGFyc2VySW50ZXJwb2xhdGlvbihJbnRlcnBv bGF0aW9uQmFzZSk6CiAgICBkZWYgaW50ZXJwb2xhdGUoc2VsZiwgdmFsdWUpOgogICAgICAgICMg Li4uCgpjbGFzcyBUZW1wbGF0ZUludGVycG9sYXRpb24oSW50ZXJwb2xhdGlvbkJhc2UpOgogICAg ZGVmIGludGVycG9sYXRlKHNlbGYsIHZhbHVlKToKICAgICAgICAjIC4uLgoKaW50ZXJwb2xhdGVf aW1wbGVtZW50YXRpb25zID0gewogICAgJ0NvbmZpZ1BhcnNlcic6IENvbmZpZ1BhcnNlckludGVy cG9sYXRpb24sCiAgICAnVGVtcGxhdGUnOiBUZW1wbGF0ZUludGVycG9sYXRpb24sCiAgICBUcnVl OiBDb25maWdQYXJzZXJJbnRlcnBvbGF0aW9uLCAgIyBEZWZhdWx0IGZvciBiYWNrd2FyZHMgY29t cGF0aWJpbGl0eQp9CgpjbGFzcyBTZWN0aW9uKGRpY3QpOgogICAgZGVmIF9faW5pdF9fKHNlbGYs IC4uLik6CiAgICAgICAgIyAuLi4KICAgICAgICBpZiBzZWxmLm1haW4uaW50ZXJwb2xhdGlvbjoK ICAgICAgICBzZWxmLl9pbnRlcnBvbGF0aW9uX2VuZ2luZSA9CmludGVycG9sYXRlX2ltcGxlbWVu dGF0aW9uc1tzZWxmLm1haW4uaW50ZXJwb2xhdGlvbl0oc2VsZikKICAgICAgICBzZWxmLl9pbnRl cnBvbGF0ZSA9IHNlbGYuX2ludGVycG9sYXRpb25fZW5naW5lLmludGVycG9sYXRlCgoKClRoZSBj dXJyZW50IF9pbnRlcnBvbGF0ZSgpIGFuZCBfaW50ZXJwb2xhdGlvbl9yZXBsYWNlKCkgZnVuY3Rp b25zCndvdWxkIG1vdmUgdG8gdGhlIENvbmZpZ1BhcnNlckludGVycG9sYXRpb24gY2xhc3MgKHdo aWNoIHdvdWxkIHVzZQpzZWxmLnNlY3Rpb24uZm9vIHdoZXJldmVyIHRoZSBjdXJyZW50IGZ1bmN0 aW9ucyB1c2Ugc2VsZi5mb28pLiBUaGUgbmV3ClRlbXBsYXRlSW50ZXJwb2xhdGlvbiBjbGFzcyB3 b3VsZCB0aGVuIHNsb3QgbmVhdGx5IGludG8gcGxhY2Ugd2l0aAptaW5pbWFsIGNoYW5nZXMgdG8g dGhlIHJlc3Qgb2YgdGhlIGNvZGUuCgpJZiB5b3UgdGhpbmsgdGhpcyBsb29rcyBkb2FibGUsIEkn bGwgYmUgaGFwcHkgdG8gc3VibWl0IHRoaXMgYXMgYSByZWFsCnBhdGNoLCB3aXRoIGEgd29ya2lu ZyBUZW1wbGF0ZUludGVycG9sYXRpb24gY2xhc3MuIChXaGljaCBJIHdvdWxkLApuYXR1cmFsbHks IHN0ZWFsIGZyb20gdGhlIFB5dGhvbiBzdGRsaWIpLgoKPiBJdCBjb3VsZCBwcm9iYWJseSBiZSBp bXBsZW1lbnRlZCBhcyBhIHNlcGFyYXRlIGZ1bmN0aW9uIHRoYXQgdXNlcwo+IGBgd2Fsa2BgIHRv IGRvIHRoZSBzdWJzdGl0dXRpb24uCgpUaGF0J3MgYW5vdGhlciBwb3NzaWJpbGl0eSwgb2YgY291 cnNlLiBJIHRoaW5rIGEgc2xvdC1pbiByZXBsYWNlbWVudApmb3IgU2VjdGlvbi5faW50ZXJwb2xh dGUoKSBhbmQgU2VjdGlvbi5faW50ZXJwb2xhdGlvbl9yZXBsYWNlKCkgd291bGQKcHJvYmFibHkg cmVzdWx0IGluIGNsZWFuZXIgY29kZSwgdGhvdWdoLgoKPiBJZiB5b3UgZ2V0IHRoaXMgd29ya2lu ZyBJIHdvdWxkIGJlIGludGVyZXN0ZWQgaW4gc2VlaW5nIGl0LiAoQW5kIGhhcHB5Cj4gdG8gcHV0 IGEgcmVmZXJlbmNlIHRvIGl0IGluIHRoZSBkb2NzLikKPgo+IEZ1enp5bWFuCj4gaHR0cDovL3d3 dy52b2lkc3BhY2Uub3JnLnVrL3B5dGhvbi9zaGFyZXdhcmUuc2h0bWwKCi0tIApSb2JpbiBNdW5u ClJvYmluLk11bm5AZ21haWwuY29tCkdQRyBrZXkgMHhENjQ5NzAxNAo= |
|
From: Robin M. <rob...@gm...> - 2006-05-22 14:14:44
|
T24gNS8yMi8wNiwgUm9iaW4gTXVubiA8cm9iaW4ubXVubkBnbWFpbC5jb20+IHdyb3RlOgo+IE1h aW50YWluaW5nIHR3byBkaWZmZXJlbnQgaW50ZXJwb2xhdGlvbiBzeW50YXhlcyBkb2VzIHNvdW5k IGxpa2UgYSBiaXQKPiBvZiBhIGNoYWxsZW5nZSwgSSBhZ3JlZS4gOi0pIEFsbG93aW5nICpib3Ro KiB0byBiZSB1c2VkIGluIHRoZSBzYW1lCj4gY29uZmlndXJhdGlvbiBmaWxlIHdvdWxkIGluZGVl ZCBiZSBhIG5pZ2h0bWFyZTsgYnV0IEkgdGhpbmsgdGhlcmUncyBhCj4gY2xlYW4gd2F5IHRvIGRv IGl0LiBFLmcuLDoKCkkgc2hvdWxkIGNsYXJpZnkuIEkgbWVhbiAidGhlcmUncyBhIGNsZWFuIHdh eSB0byBtYWludGFpbiB0d28KaW50ZXJwb2xhdGlvbiBzeW50YXhlcyIsIG5vdCAidGhlcmUncyBh IGNsZWFuIHdheSB0byBhbGxvdyBib3RoCnN5bnRheGVzIHRvIGJlIHVzZWQgaW4gdGhlIHNhbWUg Y29uZmlnIGZpbGUiLiBBbGxvd2luZyB0d28gc3ludGF4ZXMgaW4KdGhlIHNhbWUgY29uZmlnIGZp bGUgd291bGQgYmUgZXh0cmVtZWx5IHVnbHksIGFuZCBJJ20gbm90IHN1Z2dlc3RpbmcKaXQuIEkn bSBzdWdnZXN0aW5nIHVzaW5nIHRoZSAiaW50ZXJwb2xhdGlvbiIgYXR0cmlidXRlIGFzIGEga2lu ZCBvZgp0aHJlZS13YXkgc3dpdGNoLCB3aXRoICJPZmYiLCAiQSIsIGFuZCAiQiIgcG9zaXRpb25z LgoKSW5zdGVhZCBvZiBtYWtpbmcgdGhlIHVzZXIgc3BlY2lmeSBhIHN0cmluZyBhcyB0aGUgdmFs dWUsIHRoZXkgY291bGQKYWxzbyAocG9zc2libHkpIHNwZWNpZnkgImNvbmZpZ29iai5DT05GSUdQ QVJTRVIiIGFuZAoiY29uZmlnb2JqLlRFTVBMQVRFIiBpbnN0ZWFkLiAoV2hlcmUgdGhvc2UgdHdv IHZhbHVlcyB3b3VsZCBiZSB0aGUKYXBwcm9wcmlhdGUgc3RyaW5ncywgIkNvbmZpZ1BhcnNlciIg YW5kICJUZW1wbGF0ZSIpLiBUaGVyZSdzIG5vIHJlYWwKZ2FpbiB0byB0aGlzIG92ZXIganVzdCBz cGVjaWZ5aW5nIHRoZSBzdHJpbmdzIGRpcmVjdGx5LCBidXQgaXQgY2FuCmxvb2sgYmV0dGVyIGlu IHRoZSBkb2NzIHRvIHVzZSBlbnVtLWxpa2UgY29uc3RhbnRzLiAoQW5kIGl0IHdvdWxkCmVsaW1p bmF0ZSB0aGUgbmVlZCB0byB3b25kZXIgInNob3VsZCBJIGJlIGNhc2Utc2Vuc2l0aXZlIGluCmlu dGVycHJldGluZyB0aGUgImludGVycG9sYXRlIiBhdHRyaWJ1dGU/IikuCgotLSAKUm9iaW4gTXVu bgpSb2Jpbi5NdW5uQGdtYWlsLmNvbQpHUEcga2V5IDB4RDY0OTcwMTQK |
|
From: Robin M. <rob...@gm...> - 2006-05-22 16:41:20
|
T24gNS8yMi8wNiwgUm9iaW4gTXVubiA8cm9iaW4ubXVubkBnbWFpbC5jb20+IHdyb3RlOgo+IElm IHlvdSB0aGluayB0aGlzIGxvb2tzIGRvYWJsZSwgSSdsbCBiZSBoYXBweSB0byBzdWJtaXQgdGhp cyBhcyBhIHJlYWwKPiBwYXRjaCwgd2l0aCBhIHdvcmtpbmcgVGVtcGxhdGVJbnRlcnBvbGF0aW9u IGNsYXNzLiAoV2hpY2ggSSB3b3VsZCwKPiBuYXR1cmFsbHksIHN0ZWFsIGZyb20gdGhlIFB5dGhv biBzdGRsaWIpLgoKSSd2ZSBmaW5pc2hlZCB3cml0aW5nIHVwIGFuIGltcGxlbWVudGF0aW9uIG9m IFRlbXBsYXRlSW50ZXJwb2xhdGlvbi4KSXQgYWRkcyBhYm91dCA3NSBsaW5lcyBvZiBjb2RlIHRv IGNvbmZpZ29iai5weS4gT25jZSBJJ3ZlIHRlc3RlZCBpdCwKSSdsbCBzdWJtaXQgdGhlIHBhdGNo IHRvIHRoZSBjb25maWdvYmotZGV2ZWxvcCBsaXN0LgoKLS0gClJvYmluIE11bm4KUm9iaW4uTXVu bkBnbWFpbC5jb20KR1BHIGtleSAweEQ2NDk3MDE0Cg== |
|
From: Michael F. <fuz...@vo...> - 2006-05-22 21:31:05
|
Robin Munn wrote:
> On 5/22/06, Robin Munn <rob...@gm...> wrote:
>> If you think this looks doable, I'll be happy to submit this as a real
>> patch, with a working TemplateInterpolation class. (Which I would,
>> naturally, steal from the Python stdlib).
>
> I've finished writing up an implementation of TemplateInterpolation.
> It adds about 75 lines of code to configobj.py. Once I've tested it,
> I'll submit the patch to the configobj-develop list.
>
As I mentioned before I prefer the proposed substitution syntax. However
for the reasons mentioned we would have to permit both.
This has the following disadvantages :
1) Writing the code
2) Writing tests
3) Documenting the changes
4) Maintaining the code
5) A bigger code base
6) A steeper/more confusing learning curve for users, especially the
change to an option that takes a text value instead of True/False [#]_
It sounds like you are offering to help with 1 and 2 (and hopefully 3
?). I don't really mind 4, but 5 and 6 are real issues.
I appreciate the patch you are writing. Let me check with the other main
users of ConfigObj and see what they think about the change.
What is the application you would use this for ?
Fuzzyman
http://www.voidspace.org.uk/python/index.shtml
.. [#] Note that for backwards compatibility we would have to make True
a shorthand for 'ConfigParser' (possibly a deprecated way of setting it).
|
|
From: Michael F. <fuz...@vo...> - 2006-05-22 21:35:21
|
Michael Foord wrote: > Robin Munn wrote: >> On 5/22/06, Robin Munn <rob...@gm...> wrote: >>> If you think this looks doable, I'll be happy to submit this as a real >>> patch, with a working TemplateInterpolation class. (Which I would, >>> naturally, steal from the Python stdlib). >> >> I've finished writing up an implementation of TemplateInterpolation. >> It adds about 75 lines of code to configobj.py. Once I've tested it, >> I'll submit the patch to the configobj-develop list. >> > [snip..] > > .. [#] Note that for backwards compatibility we would have to make > True a shorthand for 'ConfigParser' (possibly a deprecated way of > setting it). > Oops... I see you already covered that. :-) Fuzzy http://www.voidspace.org.uk/python/index.shtml > > ------------------------------------------------------- > Using Tomcat but need to do more? Need to support web services, security? > Get stuff done quickly with pre-integrated technology to make your job > easier > Download IBM WebSphere Application Server v.1.0.1 based on Apache > Geronimo > http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 > _______________________________________________ > Configobj-develop mailing list > Con...@li... > https://lists.sourceforge.net/lists/listinfo/configobj-develop > |
|
From: Robin M. <rob...@gm...> - 2006-05-23 01:26:46
Attachments:
template-interpolation.patch
|
T24gNS8yMi8wNiwgTWljaGFlbCBGb29yZCA8ZnV6enltYW5Adm9pZHNwYWNlLm9yZy51az4gd3Jv dGU6Cj4gUm9iaW4gTXVubiB3cm90ZToKPiA+IE9uIDUvMjIvMDYsIFJvYmluIE11bm4gPHJvYmlu Lm11bm5AZ21haWwuY29tPiB3cm90ZToKPiA+PiBJZiB5b3UgdGhpbmsgdGhpcyBsb29rcyBkb2Fi bGUsIEknbGwgYmUgaGFwcHkgdG8gc3VibWl0IHRoaXMgYXMgYSByZWFsCj4gPj4gcGF0Y2gsIHdp dGggYSB3b3JraW5nIFRlbXBsYXRlSW50ZXJwb2xhdGlvbiBjbGFzcy4gKFdoaWNoIEkgd291bGQs Cj4gPj4gbmF0dXJhbGx5LCBzdGVhbCBmcm9tIHRoZSBQeXRob24gc3RkbGliKS4KPiA+Cj4gPiBJ J3ZlIGZpbmlzaGVkIHdyaXRpbmcgdXAgYW4gaW1wbGVtZW50YXRpb24gb2YgVGVtcGxhdGVJbnRl cnBvbGF0aW9uLgo+ID4gSXQgYWRkcyBhYm91dCA3NSBsaW5lcyBvZiBjb2RlIHRvIGNvbmZpZ29i ai5weS4gT25jZSBJJ3ZlIHRlc3RlZCBpdCwKPiA+IEknbGwgc3VibWl0IHRoZSBwYXRjaCB0byB0 aGUgY29uZmlnb2JqLWRldmVsb3AgbGlzdC4KPiA+Cj4gQXMgSSBtZW50aW9uZWQgYmVmb3JlIEkg cHJlZmVyIHRoZSBwcm9wb3NlZCBzdWJzdGl0dXRpb24gc3ludGF4LiBIb3dldmVyCj4gZm9yIHRo ZSByZWFzb25zIG1lbnRpb25lZCB3ZSB3b3VsZCBoYXZlIHRvIHBlcm1pdCBib3RoLgo+Cj4gVGhp cyBoYXMgdGhlIGZvbGxvd2luZyBkaXNhZHZhbnRhZ2VzIDoKPgo+ICAgICAxKSBXcml0aW5nIHRo ZSBjb2RlCj4gICAgIDIpIFdyaXRpbmcgdGVzdHMKPiAgICAgMykgRG9jdW1lbnRpbmcgdGhlIGNo YW5nZXMKPiAgICAgNCkgTWFpbnRhaW5pbmcgdGhlIGNvZGUKPiAgICAgNSkgQSBiaWdnZXIgY29k ZSBiYXNlCj4gICAgIDYpIEEgc3RlZXBlci9tb3JlIGNvbmZ1c2luZyBsZWFybmluZyBjdXJ2ZSBm b3IgdXNlcnMsIGVzcGVjaWFsbHkgdGhlCj4gY2hhbmdlIHRvIGFuIG9wdGlvbiB0aGF0IHRha2Vz IGEgdGV4dCB2YWx1ZSBpbnN0ZWFkIG9mIFRydWUvRmFsc2UgWyNdXwo+Cj4gSXQgc291bmRzIGxp a2UgeW91IGFyZSBvZmZlcmluZyB0byBoZWxwIHdpdGggMSBhbmQgMiAoYW5kIGhvcGVmdWxseSAz Cj4gPykuIEkgZG9uJ3QgcmVhbGx5IG1pbmQgNCwgYnV0IDUgYW5kIDYgYXJlIHJlYWwgaXNzdWVz LgoKSW5kZWVkLiBIb3dldmVyLCB0aGlzIGlzIHRydWUgb2YganVzdCBhYm91dCBhbnkgbmV3IGZl YXR1cmUuIFRoZSByZWFsCnF1ZXN0aW9uIGlzIHdoZXRoZXIgdGhlIGxhcmdlciBjb2RlIGJhc2Ug YW5kL29yIHN0ZWVwZXIgbGVhcm5pbmcgY3VydmUKYXJlIHdvcnRoIHRoZSBiZW5lZml0cyB0aGUg bmV3IGZlYXR1cmUgd291bGQgYnJpbmcuIEluIHRoaXMgY2FzZSwgSQp0aGluayB0aGV5IGFyZSwg YXMgSSdsbCBleHBsYWluLgoKQlRXLCBJJ3ZlIGluY2x1ZGVkIGRvY3VtZW50YXRpb24gaW4gbXkg cGF0Y2ggYXMgd2VsbCBub3csIHdoaWNoIHRha2VzCmNhcmUgb2YgMykuIEluIHRoZSBwcm9jZXNz LCBJIGZpeGVkIGEgZG9jdW1lbnRhdGlvbiBidWcuIChUaGUKIkF0dHJpYnV0ZXMiIHNlY3Rpb24s IHNlY3Rpb24gNC40LCBzYWlkIHRoZSAiaW50ZXJwb2xhdGUiIGF0dHJpYnV0ZQp3YXMgdXNlZCB0 byBzd2l0Y2ggaW50ZXJwb2xhdGlvbiBvbiBvciBvZmYsIGJ1dCBpdCdzIGFjdHVhbGx5IHRoZQoi aW50ZXJwb2xhdGlvbiIgYXR0cmlidXRlLikKCk5vdGUgdGhhdCBteSBkb2N1bWVudGF0aW9uIHBh dGNoIGxlYXZlcyB5b3Ugd2l0aCBhIG1ham9yIHNlY3Rpb24gKmFuZCoKYSBzdWJzZWN0aW9uLCBi b3RoIGNhbGxlZCAiSW50ZXJwb2xhdGlvbiIgKGFsdGhvdWdoIG9uZSBpcyBpbgpsb3dlcmNhc2Up LiBJIGRvbid0IGtub3cgaG93IHRoZSByZVN0cnVjdHVyZWRUZXh0IGludGVybmFsIGxpbmtzIChl LmcuCiJpbnRlcnBvbGF0aW9uXyIpIHdpbGwgZGVhbCB3aXRoIHRoYXQ7IHlvdSBtYXkgd2FudCB0 byBjb25zaWRlcgpyZW5hbWluZyBvbmUgb2YgdGhvc2Ugc2VjdGlvbnMsIHBlcmhhcHMgdG8gIlN0 cmluZyBJbnRlcnBvbGF0aW9uIi4KClRvIGFkZHJlc3MgNSksIGFuZCB0aGVyZWJ5IGFkZHJlc3Mg NCkgYXMgd2VsbDogbXkgcGF0Y2ggYWRkcyA4NQphZGRpdGlvbmFsIGxpbmVzIG9mIGNvZGUsIGlu Y2x1ZGluZyBjb21tZW50cy4gKFRoZSA3NSBsaW5lcyBvZiBjb2RlCmZpZ3VyZSBJIHF1b3RlZCBh Ym92ZSB3YXMgYmVmb3JlIEkgY29tbWVudGVkIHRoZSBjb2RlIHRvIG15CnNhdGlzZmFjdGlvbiku IE1vc3Qgb2YgQ29uZmlnT2JqJ3MgMjAwMCBsaW5lcyBvZiBjb2RlIGRpZG4ndCBuZWVkIHRvCmJl IHRvdWNoZWQsIHNvIHRoZSBuZXcgZmVhdHVyZSBzaG91bGQgYmUgZWFzeSB0byB1bmRlcnN0YW5k IGFuZCB0aGUKY29kZWJhc2UncyBtYW50YWluYWJpbGl0eSBzaG91bGRuJ3Qgc3VmZmVyLiBJJ3Zl IGF0dGFjaGVkIHRoZSBwYXRjaCwKc28geW91IGNhbiByZXZpZXcgaXQgZm9yIHlvdXJzZWxmIGFu ZCBkZWNpZGUgaG93IGVhc3kgb3IgZGlmZmljdWx0IGl0CndvdWxkIGJlIHRvIG1haW50YWluLiAo SSd2ZSB0ZXN0ZWQgaXQgYW5kLCBJIGJlbGlldmUsIGdvdHRlbiBhbGwgdGhlCmJ1Z3Mgd29ya2Vk IG91dCkuCgpUbyBhZGRyZXNzIDYpLCB0aGUgcXVlc3Rpb24gb2Ygc3RlZXBlciBsZWFybmluZyBj dXJ2ZTogdGhhdCdzIG1vc3RseSBhCmZhY3RvciBvZiBob3cgZ29vZCB0aGUgZG9jdW1lbnRhdGlv biBpcy4gSSd2ZSB0cmllZCB0byB3cml0ZQpkb2N1bWVudGF0aW9uIHRoYXQncyBjbGVhciB5ZXQg Y29uY2lzZS4gT25lIGFkdmFudGFnZSBoZXJlIGlzIHRoYXQgdGhlCm5ldyBmZWF0dXJlJ3Mgc3lu dGF4LCAke25hbWV9LCBpcyB0aGUgc2FtZSBzeW50YXggdGhhdCBzaGVsbCBzY3JpcHRzCnVzZSwg c28gaXQgc2hvdWxkIGFscmVhZHkgYmUgZmFtaWxpYXIgdG8gbW9zdCBkZXZlbG9wZXJzLiBUaGlz IHdpbGwKaGF2ZSB0aGUgZWZmZWN0IG9mIGZsYXR0ZW5pbmcgb3V0IHRoZSBsZWFybmluZyBjdXJ2 ZSBmb3IgdGhpcyBuZXcKZmVhdHVyZSBpbW1lbnNlbHk6IG1vc3QgZGV2ZWxvcGVycyB3aWxsIHNl ZSB0aGlzLCBzYXkgIk9oLCBpdCdzIGp1c3QKbGlrZSBzaGVsbCBzeW50YXguIEdvdCBpdCwiIGFu ZCBtb3ZlIG9uLCB3aXRoIHRoZSBuZXcgZmVhdHVyZSBmdWxseQpncmFzcGVkLgoKVGhhdCdzIGFi b3V0IGFzIGZhciBhcyBkaXNjdXNzaW9uIGNhbiB0YWtlIHVzIHdpdGhvdXQgYWN0dWFsIGNvZGUg dG8KbG9vayBhdCwgc28gSSd2ZSBhdHRhY2hlZCBteSBwYXRjaC4gTGV0IG1lIGtub3cgd2hhdCB5 b3UgdGhpbmsgb2YgaXQuCihBbmQgZmVlbCBmcmVlIHRvIHJld3JpdGUgYW55dGhpbmcsIGVzcGVj aWFsbHkgdGhlIGRvY3VtZW50YXRpb24Kc2VjdGlvbnMgaWYgeW91IHRoaW5rIHRoZXkncmUgdW5j bGVhcikuCgo+IEkgYXBwcmVjaWF0ZSB0aGUgcGF0Y2ggeW91IGFyZSB3cml0aW5nLiBMZXQgbWUg Y2hlY2sgd2l0aCB0aGUgb3RoZXIgbWFpbgo+IHVzZXJzIG9mIENvbmZpZ09iaiBhbmQgc2VlIHdo YXQgdGhleSB0aGluayBhYm91dCB0aGUgY2hhbmdlLgo+Cj4gV2hhdCBpcyB0aGUgYXBwbGljYXRp b24geW91IHdvdWxkIHVzZSB0aGlzIGZvciA/CgpJIGRvbid0IGN1cnJlbnRseSBoYXZlIGEgc3Bl Y2lmaWMgYXBwbGljYXRpb24gdGhhdCBJJ20gcGxhbm5pbmcgdG8gdXNlCkNvbmZpZ09iaiBmb3Is IGJ1dCB0aGVyZSBhcmUgc2V2ZXJhbCBUdXJib0dlYXJzIGFwcCBpZGVhcyBib3VuY2luZwphcm91 bmQgaW4gbXkgaGVhZCByaWdodCBub3cuIEFuZCBzaW5jZSBUdXJib0dlYXJzIGlzIGFwcGFyZW50 bHkgZ29pbmcKdG8gdXNlIENvbmZpZ09iaiBzdGFydGluZyBpbiBUdXJib0dlYXJzIDEuMSwgSSB3 YW50IHRvIGdldCBteQpwcmVmZXJyZWQgaW50ZXJwb2xhdGlvbiBzeW50YXggaW50byBDb25maWdP YmogYXMgc29vbiBhcyBwb3NzaWJsZSwKYmVmb3JlIEkgaGF2ZSB0byB1c2UgaXQuCgotLSAKUm9i aW4gTXVubgpSb2Jpbi5NdW5uQGdtYWlsLmNvbQpHUEcga2V5IDB4RDY0OTcwMTQK |
|
From: Nicola L. <ni...@te...> - 2006-05-23 06:33:47
|
> The real question is whether the larger code base and/or steeper
> learning curve are worth the benefits the new feature would bring. In
> this case, I think they are, as I'll explain.
I agree. The new syntax is simpler, supported by Python, and widely used
elsewhere; it seems worth the additional burden.
> My patch adds 85 additional lines of code, including comments. (The 75
> lines of code figure I quoted above was before I commented the code to
> my satisfaction). Most of ConfigObj's 2000 lines of code didn't need to
> be touched, so the new feature should be easy to understand and the
> codebase's mantainability shouldn't suffer. I've attached the patch, so
> you can review it for yourself and decide how easy or difficult it would
> be to maintain. (I've tested it and, I believe, gotten all the bugs
> worked out).
Minimizing the patch length is good, but it's even better to reuse already
tested and deployed code: therefore I'd go for a different implementation
strategy.
Instead of reimplementing parts of string.Template, I'd copy it wholesale
from the stdlib string module to an additional module, and do a conditional
import, like this:
try:
from string import Template
except ImportError:
from configobj.string import Template
That way, when ConfigObj drops support for the older Python versions, the
additional module and the conditional import will be removed, and no code
duplication will remain.
--
Nicola Larosa - http://www.tekNico.net/
As for the music factories--a.k.a. the major record companies--what they
want is power. They will never accept P2P sharing as long as it remains
a way to escape from their power. For their abuses against the people,
they deserve to be abolished, and that should be everyone's goal.
-- Richard Stallman, February 2006
|
|
From: Robin M. <rob...@gm...> - 2006-05-23 10:04:12
|
T24gNS8yMy8wNiwgTmljb2xhIExhcm9zYSA8bmljb0B0ZWtuaWNvLm5ldD4gd3JvdGU6Cj4gPiBU aGUgcmVhbCBxdWVzdGlvbiBpcyB3aGV0aGVyIHRoZSBsYXJnZXIgY29kZSBiYXNlIGFuZC9vciBz dGVlcGVyCj4gPiBsZWFybmluZyBjdXJ2ZSBhcmUgd29ydGggdGhlIGJlbmVmaXRzIHRoZSBuZXcg ZmVhdHVyZSB3b3VsZCBicmluZy4gSW4KPiA+IHRoaXMgY2FzZSwgSSB0aGluayB0aGV5IGFyZSwg YXMgSSdsbCBleHBsYWluLgo+Cj4gSSBhZ3JlZS4gVGhlIG5ldyBzeW50YXggaXMgc2ltcGxlciwg c3VwcG9ydGVkIGJ5IFB5dGhvbiwgYW5kIHdpZGVseSB1c2VkCj4gZWxzZXdoZXJlOyBpdCBzZWVt cyB3b3J0aCB0aGUgYWRkaXRpb25hbCBidXJkZW4uCj4KPgo+ID4gTXkgcGF0Y2ggYWRkcyA4NSBh ZGRpdGlvbmFsIGxpbmVzIG9mIGNvZGUsIGluY2x1ZGluZyBjb21tZW50cy4gKFRoZSA3NQo+ID4g bGluZXMgb2YgY29kZSBmaWd1cmUgSSBxdW90ZWQgYWJvdmUgd2FzIGJlZm9yZSBJIGNvbW1lbnRl ZCB0aGUgY29kZSB0bwo+ID4gbXkgc2F0aXNmYWN0aW9uKS4gTW9zdCBvZiBDb25maWdPYmoncyAy MDAwIGxpbmVzIG9mIGNvZGUgZGlkbid0IG5lZWQgdG8KPiA+IGJlIHRvdWNoZWQsIHNvIHRoZSBu ZXcgZmVhdHVyZSBzaG91bGQgYmUgZWFzeSB0byB1bmRlcnN0YW5kIGFuZCB0aGUKPiA+IGNvZGVi YXNlJ3MgbWFudGFpbmFiaWxpdHkgc2hvdWxkbid0IHN1ZmZlci4gSSd2ZSBhdHRhY2hlZCB0aGUg cGF0Y2gsIHNvCj4gPiB5b3UgY2FuIHJldmlldyBpdCBmb3IgeW91cnNlbGYgYW5kIGRlY2lkZSBo b3cgZWFzeSBvciBkaWZmaWN1bHQgaXQgd291bGQKPiA+IGJlIHRvIG1haW50YWluLiAoSSd2ZSB0 ZXN0ZWQgaXQgYW5kLCBJIGJlbGlldmUsIGdvdHRlbiBhbGwgdGhlIGJ1Z3MKPiA+IHdvcmtlZCBv dXQpLgo+Cj4gTWluaW1pemluZyB0aGUgcGF0Y2ggbGVuZ3RoIGlzIGdvb2QsIGJ1dCBpdCdzIGV2 ZW4gYmV0dGVyIHRvIHJldXNlIGFscmVhZHkKPiB0ZXN0ZWQgYW5kIGRlcGxveWVkIGNvZGU6IHRo ZXJlZm9yZSBJJ2QgZ28gZm9yIGEgZGlmZmVyZW50IGltcGxlbWVudGF0aW9uCj4gc3RyYXRlZ3ku Cj4KPiBJbnN0ZWFkIG9mIHJlaW1wbGVtZW50aW5nIHBhcnRzIG9mIHN0cmluZy5UZW1wbGF0ZSwg SSdkIGNvcHkgaXQgd2hvbGVzYWxlCj4gZnJvbSB0aGUgc3RkbGliIHN0cmluZyBtb2R1bGUgdG8g YW4gYWRkaXRpb25hbCBtb2R1bGUsIGFuZCBkbyBhIGNvbmRpdGlvbmFsCj4gaW1wb3J0LCBsaWtl IHRoaXM6Cj4KPiB0cnk6Cj4gICAgIGZyb20gc3RyaW5nIGltcG9ydCBUZW1wbGF0ZQo+IGV4Y2Vw dCBJbXBvcnRFcnJvcjoKPiAgICAgZnJvbSBjb25maWdvYmouc3RyaW5nIGltcG9ydCBUZW1wbGF0 ZQo+Cj4gVGhhdCB3YXksIHdoZW4gQ29uZmlnT2JqIGRyb3BzIHN1cHBvcnQgZm9yIHRoZSBvbGRl ciBQeXRob24gdmVyc2lvbnMsIHRoZQo+IGFkZGl0aW9uYWwgbW9kdWxlIGFuZCB0aGUgY29uZGl0 aW9uYWwgaW1wb3J0IHdpbGwgYmUgcmVtb3ZlZCwgYW5kIG5vIGNvZGUKPiBkdXBsaWNhdGlvbiB3 aWxsIHJlbWFpbi4KClRoYXQgd2FzIG15IGZpcnN0IGFwcHJvYWNoLCBidXQgaXQgZGlkbid0IGFj dHVhbGx5IHdvcmsuIFRoZSBwcm9ibGVtCndhcyB0aGF0IHN0cmluZy5UZW1wbGF0ZSBleHBlY3Rz IHlvdSB0byBwYXNzIGl0IGEgc2luZ2xlIG1hcHBpbmcKKHdoZXRoZXIgaXQncyBhIGRpY3Qgb3Ig c29tZSBvdGhlciBkaWN0LWxpa2Ugb2JqZWN0LCBpdCBkb2Vzbid0Cm1hdHRlciksIGFuZCBkb2Vz bid0IGhhdmUgYW4gYXBwcm9hY2ggZm9yIHNheWluZyAiSWYgdGhpcyBtYXBwaW5nCmZhaWxzLCB0 cnkgdGhpcyBvdGhlciBtYXBwaW5nIi4gSSBzdXBwb3NlIEkgY291bGQgaGF2ZSBjYXVnaHQgdGhl CnJlc3VsdGluZyBLZXlFcnJvciBhbmQgY2FsbGVkIHN0cmluZy5UZW1wbGF0ZS5zdWJzdGl0dXRl KCkgYWdhaW4gd2l0aAp0aGUgc2Vjb25kIG1hcHBpbmcsIGJ1dCBJJ20gbm90IHN1cmUgaXQgd291 bGQgaGF2ZSB3b3JrZWQsIGFuZCBpdAp3b3VsZCBoYXZlIGxvb2tlZCBxdWl0ZSBhIGJpdCB1Z2xp ZXIgdGhhbiB3aGF0IEkgZW5kZWQgdXAgd2l0aC4KCk9UT0gsIHlvdSBoYXZlIGEgdmVyeSBnb29k IHBvaW50IGFib3V0IHVzaW5nIGFscmVhZHkgdGVzdGVkIGFuZApkZXBsb3llZCBjb2RlLiBTbyBJ J2xsIHJlaW1wbGVtZW50IHRoZSBUZW1wbGF0ZUVuZ2luZSBjbGFzcyB1c2luZwpzdHJpbmcuVGVt cGxhdGUsIHVzaW5nIHRoZSAiY2FsbCBzdWJzdGl0dXRlKCkgYSBzZWNvbmQgYW5kIHRoaXJkIHRp bWUKaWYgeW91IGdldCBLZXlFcnJvciIgYXBwcm9hY2gsIGFuZCBzZWUgaWYgaXQgd29ya3Mgb3V0 IGJldHRlci4gSSdsbApwb3N0IG15IHJlc3VsdHMgYXMgYSBzZWNvbmQgcGF0Y2ggaWYgaXQgd29y a3Mgb3V0LiAoT3IgZXZlbiBpZiwgSU1PLAppdCBkb2Vzbid0KS4KClAuUy4gQWxvbmcgdGhlIHdh eSwgSSBkaXNjb3ZlcmVkIHRoYXQgdGhlIHdheSBpbnRlcnBvbGF0aW9uIHNlbGVjdHMKdGhlIHZh bHVlcyB0byBpbnNlcnQgKG9ubHkgbG9va2luZyBhdCBERUZBVUxUIHNlY3Rpb25zKSBkb2Vzbid0 IG1hdGNoCndoYXQgSSB3YXMgZXhwZWN0aW5nLiBXaGF0IEkgZXhwZWN0ZWQgd2FzIHRoYXQgaXQg d291bGQgZmlyc3QgbG9vayBpbgp0aGUgKmN1cnJlbnQqIHNlY3Rpb24sIHRoZW4gdGhlIGN1cnJl bnQgc2VjdGlvbidzIERFRkFVTFQsIHRoZW4gdGhlCmN1cnJlbnQgc2VjdGlvbidzIHBhcmVudCwg dGhlbiB0aGUgcGFyZW50J3MgREVGQVVMVCwgYW5kIHNvIG9uIHVwIHRoZQp0cmVlLiBIb3dldmVy LCB0aGF0J3MgYSBjb21wbGV0ZWx5IHNlcGFyYXRlIGRpc2N1c3Npb24gZnJvbSB3aGV0aGVyCnN0 cmluZy5UZW1wbGF0ZS1zdHlsZSBpbnRlcnBvbGF0aW9uIHNob3VsZCBnbyBpbiwgYW5kIHNvIEkn bGwgc2F2ZQp0aGF0IG9uZSBmb3IgYSBkaWZmZXJlbnQgdGhyZWFkLgoKLS0gClJvYmluIE11bm4K Um9iaW4uTXVubkBnbWFpbC5jb20KR1BHIGtleSAweEQ2NDk3MDE0Cg== |
|
From: Robin M. <rob...@gm...> - 2006-05-23 10:56:51
|
T24gNS8yMy8wNiwgUm9iaW4gTXVubiA8cm9iaW4ubXVubkBnbWFpbC5jb20+IHdyb3RlOgo+ID4g SW5zdGVhZCBvZiByZWltcGxlbWVudGluZyBwYXJ0cyBvZiBzdHJpbmcuVGVtcGxhdGUsIEknZCBj b3B5IGl0IHdob2xlc2FsZQo+ID4gZnJvbSB0aGUgc3RkbGliIHN0cmluZyBtb2R1bGUgdG8gYW4g YWRkaXRpb25hbCBtb2R1bGUsIGFuZCBkbyBhIGNvbmRpdGlvbmFsCj4gPiBpbXBvcnQsIGxpa2Ug dGhpczoKPiA+Cj4gPiB0cnk6Cj4gPiAgICAgZnJvbSBzdHJpbmcgaW1wb3J0IFRlbXBsYXRlCj4g PiBleGNlcHQgSW1wb3J0RXJyb3I6Cj4gPiAgICAgZnJvbSBjb25maWdvYmouc3RyaW5nIGltcG9y dCBUZW1wbGF0ZQo+ID4KPiA+IFRoYXQgd2F5LCB3aGVuIENvbmZpZ09iaiBkcm9wcyBzdXBwb3J0 IGZvciB0aGUgb2xkZXIgUHl0aG9uIHZlcnNpb25zLCB0aGUKPiA+IGFkZGl0aW9uYWwgbW9kdWxl IGFuZCB0aGUgY29uZGl0aW9uYWwgaW1wb3J0IHdpbGwgYmUgcmVtb3ZlZCwgYW5kIG5vIGNvZGUK PiA+IGR1cGxpY2F0aW9uIHdpbGwgcmVtYWluLgo+Cj4gVGhhdCB3YXMgbXkgZmlyc3QgYXBwcm9h Y2gsIGJ1dCBpdCBkaWRuJ3QgYWN0dWFsbHkgd29yay4gVGhlIHByb2JsZW0KPiB3YXMgdGhhdCBz dHJpbmcuVGVtcGxhdGUgZXhwZWN0cyB5b3UgdG8gcGFzcyBpdCBhIHNpbmdsZSBtYXBwaW5nCj4g KHdoZXRoZXIgaXQncyBhIGRpY3Qgb3Igc29tZSBvdGhlciBkaWN0LWxpa2Ugb2JqZWN0LCBpdCBk b2Vzbid0Cj4gbWF0dGVyKSwgYW5kIGRvZXNuJ3QgaGF2ZSBhbiBhcHByb2FjaCBmb3Igc2F5aW5n ICJJZiB0aGlzIG1hcHBpbmcKPiBmYWlscywgdHJ5IHRoaXMgb3RoZXIgbWFwcGluZyIuIEkgc3Vw cG9zZSBJIGNvdWxkIGhhdmUgY2F1Z2h0IHRoZQo+IHJlc3VsdGluZyBLZXlFcnJvciBhbmQgY2Fs bGVkIHN0cmluZy5UZW1wbGF0ZS5zdWJzdGl0dXRlKCkgYWdhaW4gd2l0aAo+IHRoZSBzZWNvbmQg bWFwcGluZywgYnV0IEknbSBub3Qgc3VyZSBpdCB3b3VsZCBoYXZlIHdvcmtlZCwgYW5kIGl0Cj4g d291bGQgaGF2ZSBsb29rZWQgcXVpdGUgYSBiaXQgdWdsaWVyIHRoYW4gd2hhdCBJIGVuZGVkIHVw IHdpdGguCj4KPiBPVE9ILCB5b3UgaGF2ZSBhIHZlcnkgZ29vZCBwb2ludCBhYm91dCB1c2luZyBh bHJlYWR5IHRlc3RlZCBhbmQKPiBkZXBsb3llZCBjb2RlLiBTbyBJJ2xsIHJlaW1wbGVtZW50IHRo ZSBUZW1wbGF0ZUVuZ2luZSBjbGFzcyB1c2luZwo+IHN0cmluZy5UZW1wbGF0ZSwgdXNpbmcgdGhl ICJjYWxsIHN1YnN0aXR1dGUoKSBhIHNlY29uZCBhbmQgdGhpcmQgdGltZQo+IGlmIHlvdSBnZXQg S2V5RXJyb3IiIGFwcHJvYWNoLCBhbmQgc2VlIGlmIGl0IHdvcmtzIG91dCBiZXR0ZXIuIEknbGwK PiBwb3N0IG15IHJlc3VsdHMgYXMgYSBzZWNvbmQgcGF0Y2ggaWYgaXQgd29ya3Mgb3V0LiAoT3Ig ZXZlbiBpZiwgSU1PLAo+IGl0IGRvZXNuJ3QpLgoKQXR0ZW1wdGluZyB0aGlzIGFnYWluLCBJJ20g cmVtaW5kZWQgd2h5IEkgYWJhbmRvbmVkIHRoaXMgYXBwcm9hY2ggd2hlbgpJIHRyaWVkIGl0IHll c3RlcmRheS4gSXQgdHVybnMgb3V0IHRoYXQgYmVjYXVzZSBvZiB0aGUgd2F5CnN0cmluZy5UZW1w bGF0ZSBpcyB3cml0dGVuLCBJIGNhbid0IHNhZmVseSByZXBsaWNhdGUgQ29uZmlnT2JqJ3MKY3Vy cmVudCBiZWhhdmlvci4gSGVyZSdzIHdoeToKCjEpIElmIENvbmZpZ09iaiBkb2Vzbid0IGZpbmQg dGhlIHZhbHVlIGluIGFueSBvZiB0aGUgc2VjdGlvbnMgaXQKc2VhcmNoZXMgKHRoZSB0aHJlZSBE RUZBVUxUIHN1YnNlY3Rpb25zKSwgaXQgcmFpc2VzIGEKTWlzc2luZ0ludGVycG9sYXRpb25FcnJv ci4gc3RyaW5nLlRlbXBsYXRlIGNhbiBkbyB0aGUgc2FtZSB0aGluZywKc2luY2UgaXRzIHN1YnN0 aXR1dGUoKSBmdW5jdGlvbiB3aWxsIHJhaXNlIEtleUVycm9yIGlmIHRoZSBpZGVudGlmaWVyCmdp dmVuIGlzbid0IGZvdW5kIGluIHRoZSBtYXBwaW5nLiAoRS5nLiwgeW91IHRlbGwgc3RyaW5nLlRl bXBsYXRlIHRvCmludGVycG9sYXRlICIkZm9vIiBidXQgZG9uJ3QgaGF2ZSBhIGtleSAiZm9vIiBp biB0aGUgZGljdCB5b3UgaGFuZAppdCkuIFRvIGNoYWluIHRoZSBERUZBVUxUIHNlY3Rpb25zLCBJ IGNvdWxkIGp1c3QgY2hhaW4gdGhyZWUgY2FsbHMgdG8Kc3RyaW5nLlRlbXBsYXRlLnN1YnN0aXR1 dGUoKSwgb25seSByYWlzaW5nIE1pc3NpbmdJbnRlcnBvbGF0aW9uRXJyb3IKaWYgdGhlIGxhc3Qg b25lIGZhaWxzIHRvIGZpbmQgYW55IHZhbHVlcy4gU28gZmFyIHNvIGdvb2QuCgpCdXQ6IDIpIENv bmZpZ09iaiBhbHNvIHByb21pc2VzIHJlY3Vyc2l2ZSBpbnRlcnBvbGF0aW9uLCB1cCB0byB0ZW4K bGV2ZWxzIGRlZXAuIEFuZCBoZXJlIEkgcnVuIGludG8gYSBwcm9ibGVtLiBTYXkgSSB3YW50IHRv IHB1dCB0aGUKdmFsdWUgIiQxMDAiIGluIG15IGNvbmZpZ3VyYXRpb24gZmlsZS4gSSB3cml0ZSBp dCBhcyAibW9uZXkgPSAkJDEwMCIsCnNpbmNlIHN0cmluZy5UZW1wbGF0ZS1zdHlsZSBpbnRlcnBv bGF0aW9uIHByb21pc2VzIHRoYXQgYSBkb3VibGVkCmRlbGltaXRlciB3aWxsIGJlIHR1cm5lZCBp bnRvIGEgc2luZ2xlIGRlbGltaXRlciBpbiB0aGUgb3V0cHV0IGFuZApvdGhlcndpc2UgbGVmdCB1 bnRvdWNoZWQuIEhlbmNlLCAiJCQxMDAiIHNob3VsZCBiZWNvbWUgIiQxMDAiLiBCdXQKdGhlbiB0 aGUgcmVjdXJzaXZlIGludGVycG9sYXRpb24ga2lja3MgaW4sIGFuZCB0aGUgc3RyaW5nICIkMTAw IiBpcwpzZW50IGFyb3VuZCBmb3IgYW5vdGhlciB0cnkgLS0gd2hlcmUgaXQgZmFpbHMsIGJlY2F1 c2UgIjEwMCIgaXMgbm90IGEKdmFsaWQgUHl0aG9uIGlkZW50aWZpZXIuIFNvIEkgY2FuJ3QgdXNl IHN1YnN0aXR1dGUoKTsgSSBuZWVkIHRvIHVzZQpzYWZlX3N1YnN0aXR1dGUoKSwgd2hpY2ggZG9l c24ndCByYWlzZSBleGNlcHRpb25zIHdoZW4gaXQgZW5jb3VudGVycwphbiBpbnZhbGlkIHN1YnN0 aXR1dGlvbiwgYnV0IHNpbXBseSBsZWF2ZXMgaXQgYWxvbmUuIFVzaW5nCnNhZmVfc3Vic3RpdHV0 ZSgpIGluc3RlYWQgb2Ygc3Vic3RpdHV0ZSgpIHdpbGwgYWxsb3cgbWUgdG8gbGVhdmUgdGhlCiIk MTAwIiBzdHJpbmcgYWxvbmUuIEJ1dCB3YWl0IC0tIGlmIEknbSB1c2luZyBzYWZlX3N1YnN0aXR1 dGUoKSwgdGhlbgpJJ20gYWxzbyBub3QgcmFpc2luZyBhbiBleGNlcHRpb24gd2hlbiBJIHRyeSB0 byBpbnRlcnBvbGF0ZSAiJGZvbyIKd2l0aG91dCBhIGtleSAiZm9vIiBpbiB0aGUgdmFsdWVzIGRp Y3Rpb25hcnkuIE9vcHMuCgpJIGNhbid0IHNhdGlzZnkgYm90aCAxKSBhbmQgMikgYWJvdmUgYnkg dXNpbmcgdGhlIHN0cmluZy5UZW1wbGF0ZQpjbGFzcy4gU28gSSBuZWVkIHRvIHdyaXRlIG15IG93 biBpbXBsZW1lbnRhdGlvbiwgdGhhdCBjYW4gdGhyb3cgYW4KZXJyb3Igd2hlbiBpdCBzZWVzICIk Zm9vIiBidXQgd2lsbCBsZWF2ZSAiJCQxMDAiIGFsb25lLgoKQWN0dWFsbHksIGFzIEkgd2FzIHdv cmtpbmcgdGhyb3VnaCB0aGlzIGV4YW1wbGUsIEkganVzdCByZWFsaXplZCB0aGF0Cm15IHBhdGNo IHN0aWxsIGhhcyBhIGJ1ZyBpbiBpdC4gSXQgY2FuIGRlYWwgd2l0aCB0aGUgdGVtcGxhdGUgIiQk MTAwIgphbmQgcHJvZHVjZSAiJDEwMCIsIGFuZCB0aGVuIGxlYXZlIHRoYXQgdmFsdWUgYWxvbmUg LS0gYnV0IG9ubHkKYmVjYXVzZSAiMTAwIiBpc24ndCBhIHZhbGlkIFB5dGhvbiBpZGVudGlmaWVy IGFuZCB0aHVzIGRvZXNuJ3QgbWF0Y2gKdGhlIHRlbXBsYXRlIHJlZ2V4cCBhbnltb3JlLiBCdXQg d2hhdCBpZiB5b3UgcmVhbGx5LCByZWFsbHkgd2FudCB0bwpwdXQgdGhlIHdvcmQgIiRuYW1lIiBp biBvbmUgb2YgdGhlIHZhbHVlcyBvZiB5b3VyIGNvbmZpZyBmaWxlPyBEb2luZwoiJCRuYW1lIiB3 b24ndCB3b3JrLCBiZWNhdXNlIHRoYXQgaW50ZXJwcmV0cyB0byAiJG5hbWUiIGFuZCB0aGVuIGEK TWlzc2luZ0ludGVycG9sYXRpb25FcnJvciB3aWxsIGJlIHJhaXNlZCB3aGVuIFRlbXBsYXRlRW5n aW5lIGNhbid0CmZpbmQgdGhlIGtleSAibmFtZSIgaW4gYW55IG9mIHRoZSBzZWN0aW9ucyBpdCBz ZWFyY2hlcy4KCldoYXQgdGhpcyBwYXRjaCByZWFsbHkgbmVlZHMgaXMgYSB3YXkgdG8gc2F5ICJz dG9wIHRoZSBpbnRlcnBvbGF0aW9uCnJlY3Vyc2lvbiBub3csIHdlJ3JlIHRocm91Z2giLiBUaGUg b25seSBhcHByb2FjaCBJJ3ZlIGNvbWUgdXAgd2l0aCBzbwpmYXIgaXMgdG8gc2F5IHRoYXQgaWYg eW91IGVuY291bnRlciBhICQkIGluIHlvdXIgaW5wdXQsIHRoZW4gc3RvcAppbnRlcnBvbGF0aW5n LCBiZWNhdXNlIHlvdSd2ZSBqdXN0IHR1cm5lZCB0aGF0ICQkIGludG8gYSAkLiBBbmQgb24gdGhl Cm5leHQgY3ljbGUgeW91J3JlIGdvaW5nIHRvIHN0YXJ0IGludGVycG9sYXRpbmcgdGhhdCB2YWx1 ZSwgd2hlbiB0aGUKdXNlciBoYXMgY2xlYXJseSBjb21tdW5pY2F0ZWQgKGJ5IGhpcyBlc2NhcGlu ZyB0aGUgZGVsaW1pdGVyKSBoaXMKaW50ZW50IGZvciB0aGF0IHBhcnRpY3VsYXIgdmFsdWUgKm5v dCogdG8gYmUgaW50ZXJwb2xhdGVkLgoKVGhhdCBzaW1wbHkgY2FuJ3QgYmUgZG9uZSB1c2luZyBz dHJpbmcuVGVtcGxhdGUsIG5vdCBldmVuIHdpdGgKbW9ua2V5cGF0Y2hpbmcuIFdoYXQgeW91IHdv dWxkIG5lZWQgaXMgdG8gYmUgYWJsZSB0byByZWFjaCBkb3duIGluc2lkZQp0aGUgc3RyaW5nLlRl bXBsYXRlLnN1YnN0aXR1dGUoKSBmdW5jdGlvbidzICpoZWxwZXIqIGZ1bmN0aW9uIGFuZCBhZGQK YSBsaW5lIG9mIGNvZGUgdG8gc2F5ICJJZiB5b3UgZmluZCB0aGUgJ2VzY2FwZWQnIGdyb3VwIGlu IHRoaXMgbWF0Y2gKb2JqZWN0LCB0aGVuIGRvbid0IGp1c3QgcmV0dXJuIHRoZSBkZWxpbWl0ZXIs IGJ1dCAqYWxzbyogc2V0IGEgZmxhZyB0bwpsZXQgbWUga25vdy4iIFRoYXQgY2FuJ3QgYmUgZG9u ZSB3aXRob3V0IHJlc29ydGluZyB0byBieXRlY29kZSBoYWNrcy4KKE9yIHBlcmhhcHMsIGluIFB5 dGhvbiAyLjUsIEFTVCBtYW5pcHVsYXRpb24pLiBBbmQgdGhhdCdzIGV2ZW4gd29yc2UKdGhhbiBy ZXdyaXRpbmcgc3RyaW5nLlRlbXBsYXRlIG15c2VsZi4KCkF0IHRoZSBlbmQgb2YgdGhlIGRheSwg dGhlcmUncyBubyBnZXR0aW5nIGFyb3VuZCBpdCAtLSBzdHJpbmcuVGVtcGxhdGUKaXMgbm90IGRl c2lnbmVkIGZvciByZWN1cnNpdmUgaW50ZXJwb2xhdGlvbi4gSWYgd2Ugd2FudCB0byBhbGxvdwpy ZWN1cnNpdmUgaW50ZXJwb2xhdGlvbiBhbmQgZ2V0IGl0IHJpZ2h0LCB3ZSBuZWVkIHRvIHdyaXRl IHRoZSBjb2RlCm91cnNlbHZlcy4gRm9ydHVuYXRlbHksIGl0IGNvbWVzIG91dCBwcmV0dHkgc21h bGwuCgotLSAKUm9iaW4gTXVubgpSb2Jpbi5NdW5uQGdtYWlsLmNvbQpHUEcga2V5IDB4RDY0OTcw MTQK |
|
From: Fuzzyman <fuz...@vo...> - 2006-05-23 11:11:34
|
Robin Munn wrote: > On 5/23/06, Robin Munn <rob...@gm...> wrote: > >> > Instead of reimplementing parts of string.Template, I'd copy it >> wholesale >> > from the stdlib string module to an additional module, and do a >> conditional >> > import, like this: >> > >> > try: >> > from string import Template >> > except ImportError: >> > from configobj.string import Template >> > >> > That way, when ConfigObj drops support for the older Python >> versions, the >> > additional module and the conditional import will be removed, and >> no code >> > duplication will remain. >> >> That was my first approach, but it didn't actually work. The problem >> was that string.Template expects you to pass it a single mapping >> (whether it's a dict or some other dict-like object, it doesn't >> matter), and doesn't have an approach for saying "If this mapping >> fails, try this other mapping". I suppose I could have caught the >> resulting KeyError and called string.Template.substitute() again with >> the second mapping, but I'm not sure it would have worked, and it >> would have looked quite a bit uglier than what I ended up with. >> >> OTOH, you have a very good point about using already tested and >> deployed code. So I'll reimplement the TemplateEngine class using >> string.Template, using the "call substitute() a second and third time >> if you get KeyError" approach, and see if it works out better. I'll >> post my results as a second patch if it works out. (Or even if, IMO, >> it doesn't). > > > Attempting this again, I'm reminded why I abandoned this approach when > I tried it yesterday. It turns out that because of the way > string.Template is written, I can't safely replicate ConfigObj's > current behavior. Here's why: > > 1) If ConfigObj doesn't find the value in any of the sections it > searches (the three DEFAULT subsections), it raises a > MissingInterpolationError. string.Template can do the same thing, > since its substitute() function will raise KeyError if the identifier > given isn't found in the mapping. (E.g., you tell string.Template to > interpolate "$foo" but don't have a key "foo" in the dict you hand > it). To chain the DEFAULT sections, I could just chain three calls to > string.Template.substitute(), only raising MissingInterpolationError > if the last one fails to find any values. So far so good. > > But: 2) ConfigObj also promises recursive interpolation, up to ten > levels deep. And here I run into a problem. Say I want to put the > value "$100" in my configuration file. I write it as "money = $$100", > since string.Template-style interpolation promises that a doubled > delimiter will be turned into a single delimiter in the output and > otherwise left untouched. Hence, "$$100" should become "$100". But > then the recursive interpolation kicks in, and the string "$100" is > sent around for another try -- where it fails, because "100" is not a > valid Python identifier. So I can't use substitute(); I need to use > safe_substitute(), which doesn't raise exceptions when it encounters > an invalid substitution, but simply leaves it alone. Using > safe_substitute() instead of substitute() will allow me to leave the > "$100" string alone. But wait -- if I'm using safe_substitute(), then > I'm also not raising an exception when I try to interpolate "$foo" > without a key "foo" in the values dictionary. Oops. > > I can't satisfy both 1) and 2) above by using the string.Template > class. So I need to write my own implementation, that can throw an > error when it sees "$foo" but will leave "$$100" alone. > > Actually, as I was working through this example, I just realized that > my patch still has a bug in it. It can deal with the template "$$100" > and produce "$100", and then leave that value alone -- but only > because "100" isn't a valid Python identifier and thus doesn't match > the template regexp anymore. But what if you really, really want to > put the word "$name" in one of the values of your config file? Doing > "$$name" won't work, because that interprets to "$name" and then a > MissingInterpolationError will be raised when TemplateEngine can't > find the key "name" in any of the sections it searches. > > What this patch really needs is a way to say "stop the interpolation > recursion now, we're through". The only approach I've come up with so > far is to say that if you encounter a $$ in your input, then stop > interpolating, because you've just turned that $$ into a $. And on the > next cycle you're going to start interpolating that value, when the > user has clearly communicated (by his escaping the delimiter) his > intent for that particular value *not* to be interpolated. > > That simply can't be done using string.Template, not even with > monkeypatching. What you would need is to be able to reach down inside > the string.Template.substitute() function's *helper* function and add > a line of code to say "If you find the 'escaped' group in this match > object, then don't just return the delimiter, but *also* set a flag to > let me know." That can't be done without resorting to bytecode hacks. > (Or perhaps, in Python 2.5, AST manipulation). And that's even worse > than rewriting string.Template myself. > > At the end of the day, there's no getting around it -- string.Template > is not designed for recursive interpolation. If we want to allow > recursive interpolation and get it right, we need to write the code > ourselves. Fortunately, it comes out pretty small. > Hmmm... on the other hand I'm not convinced recursive interpolation is used by anyone. I don't mind it being dropped for the string templating interpolation - *if* that makes it any easier. Fuzzyman http://www.voidspace.org.uk/python/index.shtml |
|
From: Robin M. <rob...@gm...> - 2006-05-23 11:23:41
Attachments:
2nd-template-interpolation.patch
foo.ini
|
T24gNS8yMy8wNiwgRnV6enltYW4gPGZ1enp5bWFuQHZvaWRzcGFjZS5vcmcudWs+IHdyb3RlOgo+ IFJvYmluIE11bm4gd3JvdGU6Cj4KPiA+IE9uIDUvMjMvMDYsIFJvYmluIE11bm4gPHJvYmluLm11 bm5AZ21haWwuY29tPiB3cm90ZToKPiA+Cj4gPj4gPiBJbnN0ZWFkIG9mIHJlaW1wbGVtZW50aW5n IHBhcnRzIG9mIHN0cmluZy5UZW1wbGF0ZSwgSSdkIGNvcHkgaXQKPiA+PiB3aG9sZXNhbGUKPiA+ PiA+IGZyb20gdGhlIHN0ZGxpYiBzdHJpbmcgbW9kdWxlIHRvIGFuIGFkZGl0aW9uYWwgbW9kdWxl LCBhbmQgZG8gYQo+ID4+IGNvbmRpdGlvbmFsCj4gPj4gPiBpbXBvcnQsIGxpa2UgdGhpczoKPiA+ PiA+Cj4gPj4gPiB0cnk6Cj4gPj4gPiAgICAgZnJvbSBzdHJpbmcgaW1wb3J0IFRlbXBsYXRlCj4g Pj4gPiBleGNlcHQgSW1wb3J0RXJyb3I6Cj4gPj4gPiAgICAgZnJvbSBjb25maWdvYmouc3RyaW5n IGltcG9ydCBUZW1wbGF0ZQo+ID4+ID4KPiA+PiA+IFRoYXQgd2F5LCB3aGVuIENvbmZpZ09iaiBk cm9wcyBzdXBwb3J0IGZvciB0aGUgb2xkZXIgUHl0aG9uCj4gPj4gdmVyc2lvbnMsIHRoZQo+ID4+ ID4gYWRkaXRpb25hbCBtb2R1bGUgYW5kIHRoZSBjb25kaXRpb25hbCBpbXBvcnQgd2lsbCBiZSBy ZW1vdmVkLCBhbmQKPiA+PiBubyBjb2RlCj4gPj4gPiBkdXBsaWNhdGlvbiB3aWxsIHJlbWFpbi4K PiA+Pgo+ID4+IFRoYXQgd2FzIG15IGZpcnN0IGFwcHJvYWNoLCBidXQgaXQgZGlkbid0IGFjdHVh bGx5IHdvcmsuIFRoZSBwcm9ibGVtCj4gPj4gd2FzIHRoYXQgc3RyaW5nLlRlbXBsYXRlIGV4cGVj dHMgeW91IHRvIHBhc3MgaXQgYSBzaW5nbGUgbWFwcGluZwo+ID4+ICh3aGV0aGVyIGl0J3MgYSBk aWN0IG9yIHNvbWUgb3RoZXIgZGljdC1saWtlIG9iamVjdCwgaXQgZG9lc24ndAo+ID4+IG1hdHRl ciksIGFuZCBkb2Vzbid0IGhhdmUgYW4gYXBwcm9hY2ggZm9yIHNheWluZyAiSWYgdGhpcyBtYXBw aW5nCj4gPj4gZmFpbHMsIHRyeSB0aGlzIG90aGVyIG1hcHBpbmciLiBJIHN1cHBvc2UgSSBjb3Vs ZCBoYXZlIGNhdWdodCB0aGUKPiA+PiByZXN1bHRpbmcgS2V5RXJyb3IgYW5kIGNhbGxlZCBzdHJp bmcuVGVtcGxhdGUuc3Vic3RpdHV0ZSgpIGFnYWluIHdpdGgKPiA+PiB0aGUgc2Vjb25kIG1hcHBp bmcsIGJ1dCBJJ20gbm90IHN1cmUgaXQgd291bGQgaGF2ZSB3b3JrZWQsIGFuZCBpdAo+ID4+IHdv dWxkIGhhdmUgbG9va2VkIHF1aXRlIGEgYml0IHVnbGllciB0aGFuIHdoYXQgSSBlbmRlZCB1cCB3 aXRoLgo+ID4+Cj4gPj4gT1RPSCwgeW91IGhhdmUgYSB2ZXJ5IGdvb2QgcG9pbnQgYWJvdXQgdXNp bmcgYWxyZWFkeSB0ZXN0ZWQgYW5kCj4gPj4gZGVwbG95ZWQgY29kZS4gU28gSSdsbCByZWltcGxl bWVudCB0aGUgVGVtcGxhdGVFbmdpbmUgY2xhc3MgdXNpbmcKPiA+PiBzdHJpbmcuVGVtcGxhdGUs IHVzaW5nIHRoZSAiY2FsbCBzdWJzdGl0dXRlKCkgYSBzZWNvbmQgYW5kIHRoaXJkIHRpbWUKPiA+ PiBpZiB5b3UgZ2V0IEtleUVycm9yIiBhcHByb2FjaCwgYW5kIHNlZSBpZiBpdCB3b3JrcyBvdXQg YmV0dGVyLiBJJ2xsCj4gPj4gcG9zdCBteSByZXN1bHRzIGFzIGEgc2Vjb25kIHBhdGNoIGlmIGl0 IHdvcmtzIG91dC4gKE9yIGV2ZW4gaWYsIElNTywKPiA+PiBpdCBkb2Vzbid0KS4KPiA+Cj4gPgo+ ID4gQXR0ZW1wdGluZyB0aGlzIGFnYWluLCBJJ20gcmVtaW5kZWQgd2h5IEkgYWJhbmRvbmVkIHRo aXMgYXBwcm9hY2ggd2hlbgo+ID4gSSB0cmllZCBpdCB5ZXN0ZXJkYXkuIEl0IHR1cm5zIG91dCB0 aGF0IGJlY2F1c2Ugb2YgdGhlIHdheQo+ID4gc3RyaW5nLlRlbXBsYXRlIGlzIHdyaXR0ZW4sIEkg Y2FuJ3Qgc2FmZWx5IHJlcGxpY2F0ZSBDb25maWdPYmoncwo+ID4gY3VycmVudCBiZWhhdmlvci4g SGVyZSdzIHdoeToKPiA+Cj4gPiAxKSBJZiBDb25maWdPYmogZG9lc24ndCBmaW5kIHRoZSB2YWx1 ZSBpbiBhbnkgb2YgdGhlIHNlY3Rpb25zIGl0Cj4gPiBzZWFyY2hlcyAodGhlIHRocmVlIERFRkFV TFQgc3Vic2VjdGlvbnMpLCBpdCByYWlzZXMgYQo+ID4gTWlzc2luZ0ludGVycG9sYXRpb25FcnJv ci4gc3RyaW5nLlRlbXBsYXRlIGNhbiBkbyB0aGUgc2FtZSB0aGluZywKPiA+IHNpbmNlIGl0cyBz dWJzdGl0dXRlKCkgZnVuY3Rpb24gd2lsbCByYWlzZSBLZXlFcnJvciBpZiB0aGUgaWRlbnRpZmll cgo+ID4gZ2l2ZW4gaXNuJ3QgZm91bmQgaW4gdGhlIG1hcHBpbmcuIChFLmcuLCB5b3UgdGVsbCBz dHJpbmcuVGVtcGxhdGUgdG8KPiA+IGludGVycG9sYXRlICIkZm9vIiBidXQgZG9uJ3QgaGF2ZSBh IGtleSAiZm9vIiBpbiB0aGUgZGljdCB5b3UgaGFuZAo+ID4gaXQpLiBUbyBjaGFpbiB0aGUgREVG QVVMVCBzZWN0aW9ucywgSSBjb3VsZCBqdXN0IGNoYWluIHRocmVlIGNhbGxzIHRvCj4gPiBzdHJp bmcuVGVtcGxhdGUuc3Vic3RpdHV0ZSgpLCBvbmx5IHJhaXNpbmcgTWlzc2luZ0ludGVycG9sYXRp b25FcnJvcgo+ID4gaWYgdGhlIGxhc3Qgb25lIGZhaWxzIHRvIGZpbmQgYW55IHZhbHVlcy4gU28g ZmFyIHNvIGdvb2QuCj4gPgo+ID4gQnV0OiAyKSBDb25maWdPYmogYWxzbyBwcm9taXNlcyByZWN1 cnNpdmUgaW50ZXJwb2xhdGlvbiwgdXAgdG8gdGVuCj4gPiBsZXZlbHMgZGVlcC4gQW5kIGhlcmUg SSBydW4gaW50byBhIHByb2JsZW0uIFNheSBJIHdhbnQgdG8gcHV0IHRoZQo+ID4gdmFsdWUgIiQx MDAiIGluIG15IGNvbmZpZ3VyYXRpb24gZmlsZS4gSSB3cml0ZSBpdCBhcyAibW9uZXkgPSAkJDEw MCIsCj4gPiBzaW5jZSBzdHJpbmcuVGVtcGxhdGUtc3R5bGUgaW50ZXJwb2xhdGlvbiBwcm9taXNl cyB0aGF0IGEgZG91YmxlZAo+ID4gZGVsaW1pdGVyIHdpbGwgYmUgdHVybmVkIGludG8gYSBzaW5n bGUgZGVsaW1pdGVyIGluIHRoZSBvdXRwdXQgYW5kCj4gPiBvdGhlcndpc2UgbGVmdCB1bnRvdWNo ZWQuIEhlbmNlLCAiJCQxMDAiIHNob3VsZCBiZWNvbWUgIiQxMDAiLiBCdXQKPiA+IHRoZW4gdGhl IHJlY3Vyc2l2ZSBpbnRlcnBvbGF0aW9uIGtpY2tzIGluLCBhbmQgdGhlIHN0cmluZyAiJDEwMCIg aXMKPiA+IHNlbnQgYXJvdW5kIGZvciBhbm90aGVyIHRyeSAtLSB3aGVyZSBpdCBmYWlscywgYmVj YXVzZSAiMTAwIiBpcyBub3QgYQo+ID4gdmFsaWQgUHl0aG9uIGlkZW50aWZpZXIuIFNvIEkgY2Fu J3QgdXNlIHN1YnN0aXR1dGUoKTsgSSBuZWVkIHRvIHVzZQo+ID4gc2FmZV9zdWJzdGl0dXRlKCks IHdoaWNoIGRvZXNuJ3QgcmFpc2UgZXhjZXB0aW9ucyB3aGVuIGl0IGVuY291bnRlcnMKPiA+IGFu IGludmFsaWQgc3Vic3RpdHV0aW9uLCBidXQgc2ltcGx5IGxlYXZlcyBpdCBhbG9uZS4gVXNpbmcK PiA+IHNhZmVfc3Vic3RpdHV0ZSgpIGluc3RlYWQgb2Ygc3Vic3RpdHV0ZSgpIHdpbGwgYWxsb3cg bWUgdG8gbGVhdmUgdGhlCj4gPiAiJDEwMCIgc3RyaW5nIGFsb25lLiBCdXQgd2FpdCAtLSBpZiBJ J20gdXNpbmcgc2FmZV9zdWJzdGl0dXRlKCksIHRoZW4KPiA+IEknbSBhbHNvIG5vdCByYWlzaW5n IGFuIGV4Y2VwdGlvbiB3aGVuIEkgdHJ5IHRvIGludGVycG9sYXRlICIkZm9vIgo+ID4gd2l0aG91 dCBhIGtleSAiZm9vIiBpbiB0aGUgdmFsdWVzIGRpY3Rpb25hcnkuIE9vcHMuCj4gPgo+ID4gSSBj YW4ndCBzYXRpc2Z5IGJvdGggMSkgYW5kIDIpIGFib3ZlIGJ5IHVzaW5nIHRoZSBzdHJpbmcuVGVt cGxhdGUKPiA+IGNsYXNzLiBTbyBJIG5lZWQgdG8gd3JpdGUgbXkgb3duIGltcGxlbWVudGF0aW9u LCB0aGF0IGNhbiB0aHJvdyBhbgo+ID4gZXJyb3Igd2hlbiBpdCBzZWVzICIkZm9vIiBidXQgd2ls bCBsZWF2ZSAiJCQxMDAiIGFsb25lLgo+ID4KPiA+IEFjdHVhbGx5LCBhcyBJIHdhcyB3b3JraW5n IHRocm91Z2ggdGhpcyBleGFtcGxlLCBJIGp1c3QgcmVhbGl6ZWQgdGhhdAo+ID4gbXkgcGF0Y2gg c3RpbGwgaGFzIGEgYnVnIGluIGl0LiBJdCBjYW4gZGVhbCB3aXRoIHRoZSB0ZW1wbGF0ZSAiJCQx MDAiCj4gPiBhbmQgcHJvZHVjZSAiJDEwMCIsIGFuZCB0aGVuIGxlYXZlIHRoYXQgdmFsdWUgYWxv bmUgLS0gYnV0IG9ubHkKPiA+IGJlY2F1c2UgIjEwMCIgaXNuJ3QgYSB2YWxpZCBQeXRob24gaWRl bnRpZmllciBhbmQgdGh1cyBkb2Vzbid0IG1hdGNoCj4gPiB0aGUgdGVtcGxhdGUgcmVnZXhwIGFu eW1vcmUuIEJ1dCB3aGF0IGlmIHlvdSByZWFsbHksIHJlYWxseSB3YW50IHRvCj4gPiBwdXQgdGhl IHdvcmQgIiRuYW1lIiBpbiBvbmUgb2YgdGhlIHZhbHVlcyBvZiB5b3VyIGNvbmZpZyBmaWxlPyBE b2luZwo+ID4gIiQkbmFtZSIgd29uJ3Qgd29yaywgYmVjYXVzZSB0aGF0IGludGVycHJldHMgdG8g IiRuYW1lIiBhbmQgdGhlbiBhCj4gPiBNaXNzaW5nSW50ZXJwb2xhdGlvbkVycm9yIHdpbGwgYmUg cmFpc2VkIHdoZW4gVGVtcGxhdGVFbmdpbmUgY2FuJ3QKPiA+IGZpbmQgdGhlIGtleSAibmFtZSIg aW4gYW55IG9mIHRoZSBzZWN0aW9ucyBpdCBzZWFyY2hlcy4KPiA+Cj4gPiBXaGF0IHRoaXMgcGF0 Y2ggcmVhbGx5IG5lZWRzIGlzIGEgd2F5IHRvIHNheSAic3RvcCB0aGUgaW50ZXJwb2xhdGlvbgo+ ID4gcmVjdXJzaW9uIG5vdywgd2UncmUgdGhyb3VnaCIuIFRoZSBvbmx5IGFwcHJvYWNoIEkndmUg Y29tZSB1cCB3aXRoIHNvCj4gPiBmYXIgaXMgdG8gc2F5IHRoYXQgaWYgeW91IGVuY291bnRlciBh ICQkIGluIHlvdXIgaW5wdXQsIHRoZW4gc3RvcAo+ID4gaW50ZXJwb2xhdGluZywgYmVjYXVzZSB5 b3UndmUganVzdCB0dXJuZWQgdGhhdCAkJCBpbnRvIGEgJC4gQW5kIG9uIHRoZQo+ID4gbmV4dCBj eWNsZSB5b3UncmUgZ29pbmcgdG8gc3RhcnQgaW50ZXJwb2xhdGluZyB0aGF0IHZhbHVlLCB3aGVu IHRoZQo+ID4gdXNlciBoYXMgY2xlYXJseSBjb21tdW5pY2F0ZWQgKGJ5IGhpcyBlc2NhcGluZyB0 aGUgZGVsaW1pdGVyKSBoaXMKPiA+IGludGVudCBmb3IgdGhhdCBwYXJ0aWN1bGFyIHZhbHVlICpu b3QqIHRvIGJlIGludGVycG9sYXRlZC4KPiA+Cj4gPiBUaGF0IHNpbXBseSBjYW4ndCBiZSBkb25l IHVzaW5nIHN0cmluZy5UZW1wbGF0ZSwgbm90IGV2ZW4gd2l0aAo+ID4gbW9ua2V5cGF0Y2hpbmcu IFdoYXQgeW91IHdvdWxkIG5lZWQgaXMgdG8gYmUgYWJsZSB0byByZWFjaCBkb3duIGluc2lkZQo+ ID4gdGhlIHN0cmluZy5UZW1wbGF0ZS5zdWJzdGl0dXRlKCkgZnVuY3Rpb24ncyAqaGVscGVyKiBm dW5jdGlvbiBhbmQgYWRkCj4gPiBhIGxpbmUgb2YgY29kZSB0byBzYXkgIklmIHlvdSBmaW5kIHRo ZSAnZXNjYXBlZCcgZ3JvdXAgaW4gdGhpcyBtYXRjaAo+ID4gb2JqZWN0LCB0aGVuIGRvbid0IGp1 c3QgcmV0dXJuIHRoZSBkZWxpbWl0ZXIsIGJ1dCAqYWxzbyogc2V0IGEgZmxhZyB0bwo+ID4gbGV0 IG1lIGtub3cuIiBUaGF0IGNhbid0IGJlIGRvbmUgd2l0aG91dCByZXNvcnRpbmcgdG8gYnl0ZWNv ZGUgaGFja3MuCj4gPiAoT3IgcGVyaGFwcywgaW4gUHl0aG9uIDIuNSwgQVNUIG1hbmlwdWxhdGlv bikuIEFuZCB0aGF0J3MgZXZlbiB3b3JzZQo+ID4gdGhhbiByZXdyaXRpbmcgc3RyaW5nLlRlbXBs YXRlIG15c2VsZi4KPiA+Cj4gPiBBdCB0aGUgZW5kIG9mIHRoZSBkYXksIHRoZXJlJ3Mgbm8gZ2V0 dGluZyBhcm91bmQgaXQgLS0gc3RyaW5nLlRlbXBsYXRlCj4gPiBpcyBub3QgZGVzaWduZWQgZm9y IHJlY3Vyc2l2ZSBpbnRlcnBvbGF0aW9uLiBJZiB3ZSB3YW50IHRvIGFsbG93Cj4gPiByZWN1cnNp dmUgaW50ZXJwb2xhdGlvbiBhbmQgZ2V0IGl0IHJpZ2h0LCB3ZSBuZWVkIHRvIHdyaXRlIHRoZSBj b2RlCj4gPiBvdXJzZWx2ZXMuIEZvcnR1bmF0ZWx5LCBpdCBjb21lcyBvdXQgcHJldHR5IHNtYWxs Lgo+ID4KPiBIbW1tLi4uIG9uIHRoZSBvdGhlciBoYW5kIEknbSBub3QgY29udmluY2VkIHJlY3Vy c2l2ZSBpbnRlcnBvbGF0aW9uIGlzCj4gdXNlZCBieSBhbnlvbmUuIEkgZG9uJ3QgbWluZCBpdCBi ZWluZyBkcm9wcGVkIGZvciB0aGUgc3RyaW5nIHRlbXBsYXRpbmcKPiBpbnRlcnBvbGF0aW9uIC0g KmlmKiB0aGF0IG1ha2VzIGl0IGFueSBlYXNpZXIuCj4KPiBGdXp6eW1hbgo+IGh0dHA6Ly93d3cu dm9pZHNwYWNlLm9yZy51ay9weXRob24vaW5kZXguc2h0bWwKCkhlcmUncyBhIHJldmlzZWQgdmVy c2lvbiBvZiB0aGUgdGVtcGxhdGUtaW50ZXJwb2xhdGlvbiBwYXRjaCB0aGF0CnN0b3BzIHJlY3Vy c2luZyB0aGUgbWludXRlIGl0IGVuY291bnRlcnMgYSBkb3VibGVkLXVwIGRlbGltaXRlci4gSXQK YWRkZWQgMTQgbGluZXMgb2YgbmV3IGNvZGU6IDYgbGluZXMgZGVhbGluZyB3aXRoIHRoZSAic3Rv cF9yZWN1cnNpb24iCmZsYWcsIGFuZCA4IGxpbmVzIG9mIGNvbW1lbnRzIGV4cGxhaW5pbmcgd2h5 IHRoZSBmbGFnIG1pZ2h0IGdldCBzZXQuCgpJJ3ZlIGdvdCBhIHByZXR0eSBjb21wbGV0ZSAiZm9v LmluaSIgZmlsZSB0aGF0IGV4ZXJjaXNlcyBtb3N0IG9mIHRoZQpmZWF0dXJlcyBpbiBDb25maWdP YmoncyBpbnRlcnBvbGF0aW9uIGJ5IG5vdy4gSSBzaG91bGQgdHVybiBpdCBpbnRvIGEKc2V0IG9m IHNlbnNpYmxlIHRlc3QgY2FzZXMgKGJhc2ljYWxseSwgcmVhZCBpbiBhIGNvbmZpZyBmaWxlIGFu ZCBtYWtlCnN1cmUgdGhlIGludGVycG9sYXRlZCBvcHRpb25zIGdldCB0aGVpciBjb3JyZWN0IHZh bHVlcyBhbmQgZG9uJ3QgdGhyb3cKZXhjZXB0aW9ucykuIEJ1dCBJJ20gbm90IHN1cmUgbXkgcmVn dWxhciB3b3JrbG9hZCB3aWxsIHN0YW5kIGFueSBtb3JlCmluYXR0ZW50aW9uIHRvZGF5LCBzbyBJ J2xsIGhhdmUgdG8gcHV0IGl0IG9mZiBmb3Igbm93LiBUaGVyZWZvcmUsIGluCmFkZGl0aW9uIHRv IHRoZSByZXZpc2VkIHBhdGNoLCBJJ20gYXR0YWNoaW5nIHRoZSAiZm9vLmluaSIgZmlsZSBJJ3Zl CmJlZW4gdXNpbmcgZm9yIHRlc3RpbmcuIElmIHNvbWVvbmUgZWxzZSB3YW50cyB0byB0dXJuIGl0 IGludG8gdGVzdApjYXNlcyBiZWZvcmUgSSBnZXQgYXJvdW5kIHRvIGl0LCBiZSBteSBndWVzdC4K ClRoZSBvbmUgdGhpbmcgdGhhdCBJIGhhdmVuJ3QgaW5jbHVkZWQgaW4gbXkgZG9jdW1lbnRhdGlv biBwYXRjaCwgdGhhdAptaWdodCBwb3NzaWJseSBiZSBzdXJwcmlzaW5nIGJlaGF2aW9yIHRvIHNv bWVib2R5IHdobyBoYXMgYW4gb2JzY3VyZQpjb3JuZXIgY2FzZSwgaXMgdGhlIGZhY3QgdGhhdCBy ZWN1cnNpb24gc3RvcHMgd2hlbiBpdCBoaXRzIGEgJCQuIFNvIGlmCnNvbWVvbmUncyBkb25lIHNv bWV0aGluZyBsaWtlOgoKZmlyc3QgPSBmb28Kc2Vjb25kID0gJCRiYXIKdGhpcmQgPSAiJGZpcnN0 IGFuZCAkc2Vjb25kIgpmb3VydGggPSAiJHRoaXJkIGFuZCAkc2Vjb25kIGFuZCAkZmlyc3QiCmZp ZnRoID0gJGZvdXJ0aAoKV2hlbiAiJGZvdXJ0aCIgaXMgaW50ZXJwb2xhdGVkLCB0aGUgcmVzdWx0 IHdpbGwgYmUgIiR0aGlyZCBhbmQgJHNlY29uZAphbmQgJGZpcnN0Ii4gVGhhdCB3aWxsIHlpZWxk ICIkZmlyc3QgYW5kICRzZWNvbmQgYW5kICQkYmFyIGFuZCBmb28iLgpXaGljaCBnaXZlcyAiZm9v IGFuZCAkJGJhciBhbmQgJGJhciBhbmQgZm9vIiAtLSBhbmQgYmVjYXVzZSBhICIkJCIKanVzdCB3 ZW50IHBhc3QsIGludGVycG9sYXRpb24gc3RvcHMgcmlnaHQgdGhlcmUuCgpUaGF0IG1pZ2h0IHN1 cnByaXNlIHNvbWVvbmUsIGJ1dCBJJ20gaGF2aW5nIGEgcmVhbCBoYXJkIHRpbWUgY29taW5nIHVw CndpdGggcmVhc29ucyB3aHkgYW55b25lIHdvdWxkIHdhbnQgdG8gZG8gc29tZXRoaW5nIHR3aXN0 ZWQgbGlrZSB0aGlzLgo6LSkgU28gdGhlICJzdG9wIHdoZW4geW91IGhpdCBhICQkIiBiZWhhdmlv ciBzaG91bGQgYmUgZmluZSBmb3IgbW9zdApwdXJwb3Nlcy4KCi0tIApSb2JpbiBNdW5uClJvYmlu Lk11bm5AZ21haWwuY29tCkdQRyBrZXkgMHhENjQ5NzAxNAo= |
|
From: Fuzzyman <fuz...@vo...> - 2006-05-23 15:13:13
|
Hello Robin, Thanks for your comments/work etc. I'll probably push out a 4.3.2 release soon with the minor bugfixes I've already done and then work on a 4.4.0 release. We can then decide how to integrate the new templating into that. The bazaar folks are concerned about ConfigObj performance and aim to create some patches to address that, hopefully they can be integrated into the same release. All the best, Fuzzyman http://www.voidspace.org.uk/python/index.shtml Robin Munn wrote: > On 5/23/06, Fuzzyman <fuz...@vo...> wrote: > >> Robin Munn wrote: >> >> > On 5/23/06, Robin Munn <rob...@gm...> wrote: >> > >> >> > Instead of reimplementing parts of string.Template, I'd copy it >> >> wholesale >> >> > from the stdlib string module to an additional module, and do a >> >> conditional >> >> > import, like this: >> >> > >> >> > try: >> >> > from string import Template >> >> > except ImportError: >> >> > from configobj.string import Template >> >> > >> >> > That way, when ConfigObj drops support for the older Python >> >> versions, the >> >> > additional module and the conditional import will be removed, and >> >> no code >> >> > duplication will remain. >> >> >> >> That was my first approach, but it didn't actually work. The problem >> >> was that string.Template expects you to pass it a single mapping >> >> (whether it's a dict or some other dict-like object, it doesn't >> >> matter), and doesn't have an approach for saying "If this mapping >> >> fails, try this other mapping". I suppose I could have caught the >> >> resulting KeyError and called string.Template.substitute() again with >> >> the second mapping, but I'm not sure it would have worked, and it >> >> would have looked quite a bit uglier than what I ended up with. >> >> >> >> OTOH, you have a very good point about using already tested and >> >> deployed code. So I'll reimplement the TemplateEngine class using >> >> string.Template, using the "call substitute() a second and third time >> >> if you get KeyError" approach, and see if it works out better. I'll >> >> post my results as a second patch if it works out. (Or even if, IMO, >> >> it doesn't). >> > >> > >> > Attempting this again, I'm reminded why I abandoned this approach when >> > I tried it yesterday. It turns out that because of the way >> > string.Template is written, I can't safely replicate ConfigObj's >> > current behavior. Here's why: >> > >> > 1) If ConfigObj doesn't find the value in any of the sections it >> > searches (the three DEFAULT subsections), it raises a >> > MissingInterpolationError. string.Template can do the same thing, >> > since its substitute() function will raise KeyError if the identifier >> > given isn't found in the mapping. (E.g., you tell string.Template to >> > interpolate "$foo" but don't have a key "foo" in the dict you hand >> > it). To chain the DEFAULT sections, I could just chain three calls to >> > string.Template.substitute(), only raising MissingInterpolationError >> > if the last one fails to find any values. So far so good. >> > >> > But: 2) ConfigObj also promises recursive interpolation, up to ten >> > levels deep. And here I run into a problem. Say I want to put the >> > value "$100" in my configuration file. I write it as "money = $$100", >> > since string.Template-style interpolation promises that a doubled >> > delimiter will be turned into a single delimiter in the output and >> > otherwise left untouched. Hence, "$$100" should become "$100". But >> > then the recursive interpolation kicks in, and the string "$100" is >> > sent around for another try -- where it fails, because "100" is not a >> > valid Python identifier. So I can't use substitute(); I need to use >> > safe_substitute(), which doesn't raise exceptions when it encounters >> > an invalid substitution, but simply leaves it alone. Using >> > safe_substitute() instead of substitute() will allow me to leave the >> > "$100" string alone. But wait -- if I'm using safe_substitute(), then >> > I'm also not raising an exception when I try to interpolate "$foo" >> > without a key "foo" in the values dictionary. Oops. >> > >> > I can't satisfy both 1) and 2) above by using the string.Template >> > class. So I need to write my own implementation, that can throw an >> > error when it sees "$foo" but will leave "$$100" alone. >> > >> > Actually, as I was working through this example, I just realized that >> > my patch still has a bug in it. It can deal with the template "$$100" >> > and produce "$100", and then leave that value alone -- but only >> > because "100" isn't a valid Python identifier and thus doesn't match >> > the template regexp anymore. But what if you really, really want to >> > put the word "$name" in one of the values of your config file? Doing >> > "$$name" won't work, because that interprets to "$name" and then a >> > MissingInterpolationError will be raised when TemplateEngine can't >> > find the key "name" in any of the sections it searches. >> > >> > What this patch really needs is a way to say "stop the interpolation >> > recursion now, we're through". The only approach I've come up with so >> > far is to say that if you encounter a $$ in your input, then stop >> > interpolating, because you've just turned that $$ into a $. And on the >> > next cycle you're going to start interpolating that value, when the >> > user has clearly communicated (by his escaping the delimiter) his >> > intent for that particular value *not* to be interpolated. >> > >> > That simply can't be done using string.Template, not even with >> > monkeypatching. What you would need is to be able to reach down inside >> > the string.Template.substitute() function's *helper* function and add >> > a line of code to say "If you find the 'escaped' group in this match >> > object, then don't just return the delimiter, but *also* set a flag to >> > let me know." That can't be done without resorting to bytecode hacks. >> > (Or perhaps, in Python 2.5, AST manipulation). And that's even worse >> > than rewriting string.Template myself. >> > >> > At the end of the day, there's no getting around it -- string.Template >> > is not designed for recursive interpolation. If we want to allow >> > recursive interpolation and get it right, we need to write the code >> > ourselves. Fortunately, it comes out pretty small. >> > >> Hmmm... on the other hand I'm not convinced recursive interpolation is >> used by anyone. I don't mind it being dropped for the string templating >> interpolation - *if* that makes it any easier. >> >> Fuzzyman >> http://www.voidspace.org.uk/python/index.shtml > > > Here's a revised version of the template-interpolation patch that > stops recursing the minute it encounters a doubled-up delimiter. It > added 14 lines of new code: 6 lines dealing with the "stop_recursion" > flag, and 8 lines of comments explaining why the flag might get set. > > I've got a pretty complete "foo.ini" file that exercises most of the > features in ConfigObj's interpolation by now. I should turn it into a > set of sensible test cases (basically, read in a config file and make > sure the interpolated options get their correct values and don't throw > exceptions). But I'm not sure my regular workload will stand any more > inattention today, so I'll have to put it off for now. Therefore, in > addition to the revised patch, I'm attaching the "foo.ini" file I've > been using for testing. If someone else wants to turn it into test > cases before I get around to it, be my guest. > > The one thing that I haven't included in my documentation patch, that > might possibly be surprising behavior to somebody who has an obscure > corner case, is the fact that recursion stops when it hits a $$. So if > someone's done something like: > > first = foo > second = $$bar > third = "$first and $second" > fourth = "$third and $second and $first" > fifth = $fourth > > When "$fourth" is interpolated, the result will be "$third and $second > and $first". That will yield "$first and $second and $$bar and foo". > Which gives "foo and $$bar and $bar and foo" -- and because a "$$" > just went past, interpolation stops right there. > > That might surprise someone, but I'm having a real hard time coming up > with reasons why anyone would want to do something twisted like this. > :-) So the "stop when you hit a $$" behavior should be fine for most > purposes. > >------------------------------------------------------------------------ > >=== docs/configobj.txt >================================================================== >--- docs/configobj.txt (revision 19655) >+++ docs/configobj.txt (local) >@@ -787,13 +787,13 @@ > * create_empty > * file_error > >-interpolate >-~~~~~~~~~~~ >+interpolation >+~~~~~~~~~~~~~ > > ConfigObj can perform string interpolation in a *similar* way to > ``ConfigParser``. See the interpolation_ section for full details. > >-If ``interpolate`` is set to ``False``, then interpolation is *not* done when >+If ``interpolation`` is set to ``False``, then interpolation is *not* done when > you fetch values. > > stringify >@@ -1943,9 +1943,29 @@ > ============= > > ConfigObj allows string interpolation *similar* to the way ``ConfigParser`` >+or ``string.Template`` work. The value of the ``interpolation`` attribute >+determines which style of interpolation you want to use. Valid values are >+"ConfigParser" or "Template" (case-insensitive, so "configparser" and >+"template" will also work). For backwards compatibility reasons, the value >+``True`` is also a valid value for the ``interpolation`` attribute, and >+will select ``ConfigParser``-style interpolation. At some undetermined point >+in the future, that default *may* change to ``Template``-style interpolation. > >-You specify a value to be substituted by including ``%(name)s`` in the value. >+For ``ConfigParser``-style interpolation, you specify a value to be >+substituted by including ``%(name)s`` in the value. > >+For ``Template``-style interpolation, you specify a value to be substituted >+by including ``${name}`` in the value. Alternately, if 'name' is a valid >+Python identifier (i.e., is composed of nothing but alphanumeric characters, >+plus the underscore character), then the braces are optional and the value >+can be written as ``$name``. >+ >+Note that ``ConfigParser``-style interpolation and ``Template``-style >+interpolation are mutually exclusive; you cannot have a configuration file >+that's a mix of one or the other. Pick one and stick to it. ``Template``-style >+interpolation is simpler to read and write by hand, and is recommended if >+you don't have a particular reason to use ``ConfigParser``-style. >+ > Interpolation checks first the 'DEFAULT' sub-section of the current section to > see if ``name`` is the key to a value. ('name' is case sensitive). > >=== pythonutils/configobj.py >================================================================== >--- pythonutils/configobj.py (revision 19655) >+++ pythonutils/configobj.py (local) >@@ -102,6 +102,7 @@ > __all__ = ( > '__version__', > 'DEFAULT_INDENT_TYPE', >+ 'DEFAULT_INTERPOLATION', > 'NUM_INDENT_SPACES', > 'MAX_INTERPOL_DEPTH', > 'ConfigObjError', >@@ -121,6 +122,7 @@ > 'flatten_errors', > ) > >+DEFAULT_INTERPOLATION = 'configparser' > DEFAULT_INDENT_TYPE = ' ' > NUM_INDENT_SPACES = 4 > MAX_INTERPOL_DEPTH = 10 >@@ -276,11 +278,114 @@ > """An error parsing in unrepr mode.""" > > >+class InterpolationEngine(object): >+ """ >+ A helper class to help perform string interpolation. >+ >+ This class is an abstract base class; its descendants perform >+ the actual work. >+ """ >+ >+ # compiled regexp to use in self.interpolate() >+ _KEYCRE = re.compile(r"%\(([^)]*)\)s") >+ >+ def __init__(self, section): >+ # the Section instance that "owns" this engine >+ self.section = section >+ # flag to tell the engine to stop looping through the recursion >+ # (can't use an exception for this, because some implementations >+ # such as TemplateEngine need to set this flag and still be able >+ # to return a value) >+ self.stop_recursion = False >+ >+ def interpolate(self, value): >+ depth = MAX_INTERPOL_DEPTH >+ # loop through this until it's done >+ self.stop_recursion = False >+ while depth: >+ depth -= 1 >+ if self._KEYCRE.search(value): >+ value = self._KEYCRE.sub(self._interpolation_replace, value) >+ else: >+ break >+ # self._interpolation_replace might have set this flag >+ if self.stop_recursion: >+ break >+ else: >+ raise InterpolationDepthError(value) >+ self.stop_recursion = False >+ return value >+ >+ def _fetch(self, name): >+ """Helper function to fetch values from owning section.""" >+ # switch off interpolation before we try and fetch anything ! >+ save_interp = self.section.main.interpolation >+ self.section.main.interpolation = False >+ # try the 'DEFAULT' member of owning section first >+ val = self.section.get('DEFAULT', {}).get(name) >+ # try the 'DEFAULT' member of owner's parent section next >+ if val is None: >+ val = self.section.parent.get('DEFAULT', {}).get(name) >+ # last, try the 'DEFAULT' member of the main section >+ if val is None: >+ val = self.section.main.get('DEFAULT', {}).get(name) >+ # restore interpolation to previous value before returning >+ self.section.main.interpolation = save_interp >+ if val is None: >+ raise MissingInterpolationOption(name) >+ return val >+ >+ def _interpolation_replace(self, match): >+ """Implementation-dependent helper function.""" >+ raise NotImplementedError >+ >+ >+class ConfigParserInterpolation(InterpolationEngine): >+ """Behaves like ConfigParser.""" >+ _KEYCRE = re.compile(r"%\(([^)]*)\)s") >+ >+ def _interpolation_replace(self, match): >+ s = match.group(1) >+ return self._fetch(s) >+ >+ >+class TemplateInterpolation(InterpolationEngine): >+ """Behaves like string.Template.""" >+ _delimiter = '$' >+ _KEYCRE = re.compile(r""" >+ \$(?: >+ (?P<escaped>\$) | # Two $ signs >+ (?P<named>[_a-z][_a-z0-9]*) | # $name format >+ {(?P<braced>[^}]*)} # ${name} format >+ ) >+ """, re.IGNORECASE | re.VERBOSE) >+ >+ def _interpolation_replace(self, match): >+ # Valid name (in or out of braces): fetch value from section >+ s = match.group('named') or match.group('braced') >+ if s is not None: >+ return self._fetch(s) >+ # Escaped delimiter (e.g., $$): return single delimiter >+ if match.group('escaped') is not None: >+ # Stop the recursive interpolation now, because the user intended >+ # this dollar sign to end up in the final result. If we go through >+ # one more level of recursion, we'll end up interpolating it. >+ self.stop_recursion = True >+ return self._delimiter >+ # Anything else: ignore completely, just return it unchanged >+ raise AssertionError, "Shouldn't get here -- invalid Template interpolation value" >+ return match.group() >+ >+interpolation_engines = { >+ 'configparser': ConfigParserInterpolation, >+ 'template': TemplateInterpolation, >+} >+ > class Section(dict): > """ > A dictionary-like object that represents a section in a config file. > >- It does string interpolation if the 'interpolate' attribute >+ It does string interpolation if the 'interpolation' attribute > of the 'main' object is set to True. > > Interpolation is tried first from the 'DEFAULT' section of this object, >@@ -293,8 +398,6 @@ > Iteration follows the order: scalars, then sections. > """ > >- _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") >- > def __init__(self, parent, depth, main, indict=None, name=None): > """ > * parent is the section above >@@ -336,40 +439,27 @@ > self[entry] = indict[entry] > > def _interpolate(self, value): >- """Nicked from ConfigParser.""" >- depth = MAX_INTERPOL_DEPTH >- # loop through this until it's done >- while depth: >- depth -= 1 >- if value.find("%(") != -1: >- value = self._KEYCRE.sub(self._interpolation_replace, value) >+ try: >+ # do we already have an interpolation engine? >+ engine = self._interpolation_engine >+ except AttributeError: >+ # not yet: first time running _interpolate(), so pick the engine >+ name = self.main.interpolation >+ if name == True: # note that "if name:" would be incorrect here >+ # backwards-compatibility: interpolation=True means use default >+ name = DEFAULT_INTERPOLATION >+ name = name.lower() # so that "Template", "template", etc. all work >+ klass = interpolation_engines.get(name, None) >+ if klass is None: >+ # invalid value for self.main.interpolation >+ self.main.interpolation = False >+ return value > else: >- break >- else: >- raise InterpolationDepthError(value) >- return value >+ # save reference to engine so we don't have to do this again >+ engine = self._interpolation_engine = klass(self) >+ # let the engine do the actual work >+ return engine.interpolate(value) > >- def _interpolation_replace(self, match): >- """ """ >- s = match.group(1) >- if s is None: >- return match.group() >- else: >- # switch off interpolation before we try and fetch anything ! >- self.main.interpolation = False >- # try the 'DEFAULT' member of *this section* first >- val = self.get('DEFAULT', {}).get(s) >- # try the 'DEFAULT' member of the *parent section* next >- if val is None: >- val = self.parent.get('DEFAULT', {}).get(s) >- # last, try the 'DEFAULT' member of the *main section* >- if val is None: >- val = self.main.get('DEFAULT', {}).get(s) >- self.main.interpolation = True >- if val is None: >- raise MissingInterpolationOption(s) >- return val >- > def __getitem__(self, key): > """Fetch the item and do string interpolation.""" > val = dict.__getitem__(self, key) > > > > > > > |
|
From: Robin M. <rob...@gm...> - 2006-06-06 01:37:42
|
On 5/23/06, Fuzzyman <fuz...@vo...> wrote:
> Hmmm... on the other hand I'm not convinced recursive interpolation is
> used by anyone. I don't mind it being dropped for the string templating
> interpolation - *if* that makes it any easier.
Seeing your blog post (where you mention that PEP-292-style substition
will be going into the next ConfigObj release) got me thinking about
recursive interpolation again. Right now the way it works is as
follows:
first = "one"
second = "$first plus one"
third = "$second plus one"
"third" is evaluated, yielding "$second plus one" -- which is then
further evaluated to yield "one plus one plus one". This is "lazy"
evaluation -- each string is only evaluated once its value is
requested.
What if this was changed to use "eager" evaluation instead, where each
string is evaluated as it goes past?
first = "one"
second = "$first plus one" -- immediately evaluated to yield "one plus one"
third = "$second plus one" -- immediately evaluated to yield "one plus
one plus one" (in one single step, since the value of $second is now
"one plus one" which needs no further recursive evaluation)
The advantage of lazy loading is that no particular order is enforced
on values. We could have written it in the order:
third = "$second plus one"
second = "$first plus one"
first = "one"
And it would have worked. That ordering would cause an error under
eager-evaluation, because "$second" is as yet undefined when "third"
is being created.
There's one other behavior of the interpolation that was surprising to
me: the fact that interpolated variables are *only* allowed to refer
to values in the [DEFAULT] section. That means that I can't do:
[my_program]
path = /home/rmunn/foo
configfile = ${path}/bar.txt
Instead, I have to do:
[DEFAULT]
path = /home/rmunn/foo
[my_program]
configfile = ${path}/bar.txt
This, IMHO, breaks the Principle of Least Surprise. This may be how
ConfigParser behaves, but is this behavior correct? I would suggest
that it isn't; that people writing config files are much more likely
to naturally write the first form than the second.
This may be two separate issues, though, so perhaps I should break the
second one out into its own thread.
--
Robin Munn
Rob...@gm...
GPG key 0xD6497014
|
|
From: Rick K. <ri...@mc...> - 2006-06-06 11:08:09
|
Hi Robin et all,
Monday, June 05, 2006, you wrote:
RM> On 5/23/06, Fuzzyman <fuz...@vo...> wrote:
>> Hmmm... on the other hand I'm not convinced recursive interpolation is
>> used by anyone. I don't mind it being dropped for the string templating
>> interpolation - *if* that makes it any easier.
RM> Seeing your blog post (where you mention that PEP-292-style substition
RM> will be going into the next ConfigObj release) got me thinking about
RM> recursive interpolation again. Right now the way it works is as
RM> follows:
RM> first =3D "one"
RM> second =3D "$first plus one"
RM> third =3D "$second plus one"
RM> "third" is evaluated, yielding "$second plus one" -- which is then
RM> further evaluated to yield "one plus one plus one". This is "lazy"
RM> evaluation -- each string is only evaluated once its value is
RM> requested.
RM> What if this was changed to use "eager" evaluation instead, where each
RM> string is evaluated as it goes past?
RM> first =3D "one"
RM> second =3D "$first plus one" -- immediately evaluated to yield "one plu=
s one"
RM> third =3D "$second plus one" -- immediately evaluated to yield "one plus
RM> one plus one" (in one single step, since the value of $second is now
RM> "one plus one" which needs no further recursive evaluation)
RM> The advantage of lazy loading is that no particular order is enforced
RM> on values. We could have written it in the order:
RM> third =3D "$second plus one"
RM> second =3D "$first plus one"
RM> first =3D "one"
RM> And it would have worked. That ordering would cause an error under
RM> eager-evaluation, because "$second" is as yet undefined when "third"
RM> is being created.
RM> There's one other behavior of the interpolation that was surprising to
RM> me: the fact that interpolated variables are *only* allowed to refer
RM> to values in the [DEFAULT] section. That means that I can't do:
Agree 100% on this.
We don't use interpolation due to the current behavior
Cheers,
TeamSTARS
Dick Gordon and Rick Kier
=20
RM> [my_program]
RM> path =3D /home/rmunn/foo
RM> configfile =3D ${path}/bar.txt
RM> Instead, I have to do:
RM> [DEFAULT]
RM> path =3D /home/rmunn/foo
RM> [my_program]
RM> configfile =3D ${path}/bar.txt
RM> This, IMHO, breaks the Principle of Least Surprise. This may be how
RM> ConfigParser behaves, but is this behavior correct? I would suggest
RM> that it isn't; that people writing config files are much more likely
RM> to naturally write the first form than the second.
RM> This may be two separate issues, though, so perhaps I should break the
RM> second one out into its own thread.
|
|
From: Fuzzyman <fuz...@vo...> - 2006-06-06 11:49:59
|
Rick Kier wrote: >Hi Robin et all, > >Monday, June 05, 2006, you wrote: > >RM> On 5/23/06, Fuzzyman <fuz...@vo...> wrote: > > >>>Hmmm... on the other hand I'm not convinced recursive interpolation is >>>used by anyone. I don't mind it being dropped for the string templating >>>interpolation - *if* that makes it any easier. >>> >>> > >RM> Seeing your blog post (where you mention that PEP-292-style substition >RM> will be going into the next ConfigObj release) got me thinking about >RM> recursive interpolation again. Right now the way it works is as >RM> follows: > >RM> first = "one" >RM> second = "$first plus one" >RM> third = "$second plus one" > >RM> "third" is evaluated, yielding "$second plus one" -- which is then >RM> further evaluated to yield "one plus one plus one". This is "lazy" >RM> evaluation -- each string is only evaluated once its value is >RM> requested. > >RM> What if this was changed to use "eager" evaluation instead, where each >RM> string is evaluated as it goes past? > >RM> first = "one" >RM> second = "$first plus one" -- immediately evaluated to yield "one plus one" >RM> third = "$second plus one" -- immediately evaluated to yield "one plus >RM> one plus one" (in one single step, since the value of $second is now >RM> "one plus one" which needs no further recursive evaluation) > >RM> The advantage of lazy loading is that no particular order is enforced >RM> on values. We could have written it in the order: > >RM> third = "$second plus one" >RM> second = "$first plus one" >RM> first = "one" > >RM> And it would have worked. That ordering would cause an error under >RM> eager-evaluation, because "$second" is as yet undefined when "third" >RM> is being created. > > >RM> There's one other behavior of the interpolation that was surprising to >RM> me: the fact that interpolated variables are *only* allowed to refer >RM> to values in the [DEFAULT] section. That means that I can't do: > >Agree 100% on this. > >We don't use interpolation due to the current behavior > > I don't use string substitution at all, so I am willing to listen to the views of users on this. It could theoretically break behaviour for others. Anyone else got opinions ? Fuzzyman http://www.voidspace.org.uk/python/index.shtml >Cheers, > >TeamSTARS > Dick Gordon and Rick Kier > >RM> [my_program] >RM> path = /home/rmunn/foo >RM> configfile = ${path}/bar.txt > >RM> Instead, I have to do: > >RM> [DEFAULT] >RM> path = /home/rmunn/foo > >RM> [my_program] >RM> configfile = ${path}/bar.txt > >RM> This, IMHO, breaks the Principle of Least Surprise. This may be how >RM> ConfigParser behaves, but is this behavior correct? I would suggest >RM> that it isn't; that people writing config files are much more likely >RM> to naturally write the first form than the second. > >RM> This may be two separate issues, though, so perhaps I should break the >RM> second one out into its own thread. > > > > > >_______________________________________________ >Configobj-develop mailing list >Con...@li... >https://lists.sourceforge.net/lists/listinfo/configobj-develop > > > |
|
From: Robin M. <rob...@gm...> - 2006-06-07 22:24:48
|
On 6/6/06, Fuzzyman <fuz...@vo...> wrote: > Rick Kier wrote: > > >Hi Robin et all, > > > >Monday, June 05, 2006, Robin Munn wrote: > >RM> There's one other behavior of the interpolation that was surprising to > >RM> me: the fact that interpolated variables are *only* allowed to refer > >RM> to values in the [DEFAULT] section. That means that I can't do: > > > >Agree 100% on this. > > > >We don't use interpolation due to the current behavior > > > > > I don't use string substitution at all, so I am willing to listen to the > views of users on this. > > It could theoretically break behaviour for others. > > Anyone else got opinions ? > > Fuzzyman > http://www.voidspace.org.uk/python/index.shtml After giving it some more thought, I'm starting to believe the best interpolation behavior is going to be eager-interpolation, pulling values from anywhere in the current section, its DEFAULTs, or its parents and their DEFAULTs. So the algorithm looks like this: When a string interpolation (in either "%(foo)s" or "$foo" syntax) is seen, immediately try to interpret it as follows: 1. Look for name "foo" in current section. If found, exit loop. 2. Look for name "foo" in current section's DEFAULTs. If found, exit loop. 3. Move "current section" reference to parent. If no parent, raise NotFoundException. 4. Repeat from step 1. Advantages of this approach: 1. No need for special-case "Stop when a $$ is seen" code. Special-case code usually makes me uncomfortable unless there's a darn good reason for it, and this one smells like a workaround. 2. The scoping rules work like I would have expected them to work in the first place: specifically-given values override default values, and parent namespaces are consulted all the way up to the top. 3. No need for recursive evaluation, or any maximumum-nesting-depth parameters. Since whatever $foo expands to has *already* been evaluated (or else the "foo" name wouldn't exist), any interpolations in the "foo" value have already been expanded. So we just replace "$foo" with the value and we're done. Disadvantages of this approach: 1. How do we round-trip? E.g., if we have "foo = 3" and "bar = $foo", after we load the config file, the value of "bar" is 3, and so when we write it out we'll get "foo = 3" and "bar = 3". Solution: if interpolation happens, keep the value of the original string around for use when we write out the config file. Question: where to keep that original string? 2. Values modified after the config file is parsed won't propagate their changes "upwards" to anything that includes them. If after parsing the file, we set "foo" to 4, "bar" will still be 3. This may not be the desired behavior for some people: they might want to change the "path_root" value and have that change propagate throughout their config structure. Solution: since we kept the original values around, when "foo" changes, we can re-evaluate whatever depends on "foo". (This would be a perfect place to use PyCells, once that project is complete). Or we could simply re-evaluate the entire config structure to be safe. Note: Some people may *not* want the value of "bar" to be changed when "foo" is modified. We should make this an option somewhere -- perhaps a "propagate_changes=False" keyword to a function call, or a "propagate_changes" attribute in the ConfigObj instance. Haven't thought about this one deeply yet. Summary: I propose eager evaluation of interpolated values, with lexical scoping for finding values. The original, non-interpolated strings should be saved for the purposes of configfile round-tripping and, optionally, post-evaluation propagation of changes to dependent values (e.g., changing the value of "bar = $foo" when "foo" is modified). -- Robin Munn Rob...@gm... GPG key 0xD6497014 |
|
From: Robin M. <rob...@gm...> - 2006-06-08 10:10:16
|
Following up on my own post: On 6/7/06, Robin Munn <rob...@gm...> wrote: > Summary: I propose eager evaluation of interpolated values, with > lexical scoping for finding values. The original, non-interpolated > strings should be saved for the purposes of configfile round-tripping > and, optionally, post-evaluation propagation of changes to dependent > values (e.g., changing the value of "bar = $foo" when "foo" is > modified). I started to try to implement the above in code, and ran into enough problems that it made me realize that this is a bad idea, and that the author of the above proposal is clearly an idiot. ;-) There's nothing wrong with lazy-loading. The *proper* way to do it, in fact, is to lazy-load the values we find, *recursively*, in a classic depth-first algorithm. (Keeping track of our backtrail, to catch any infinite loops). Thus: foo = 123 bar = $foo baz = $$foo quux = $bar + $baz Finding the final value of "quux": 1) Interpolate $bar 1a) Interpolate $foo 1a1) Find value "123" 1b) Final value of bar is "123" 2) Value of quux now "123 + $baz". Interpolate $baz 2a) Interpolate $$foo 2b) Final value of baz is "$foo" 3) Final value of quux is "123 + $foo". The original (non-interpolated) values are left in the Section dict at all times, so that every time they're loaded, they will be re-interpolated anew. This allows for "foo" to be changed, and the values of "bar" and "quux" to automatically be re-computed next time they're accessed. (But not "baz", since it doesn't depend on the value of "foo"). This will be a lot easier to implement in code than my previous proposal, since it requires fewer changes to what's already there. I'll make two patches: one with the current which-section-to-look-at behavior (only looking at sections named DEFAULT), and another with my preferred look-at-all-parent-sections behavior. -- Robin Munn Rob...@gm... GPG key 0xD6497014 |
|
From: Robin M. <rob...@gm...> - 2006-06-08 12:18:28
Attachments:
3rd-template-interpolation.patch
|
Here's the patch. This one implements a *proper* recursive, lazy-loading interpolation approach, and no longer suffers from the "stop when a $$ is seen" problem. If anything stops interpolation of one key, the rest of the keys in the string will not have their interpolation stopped. Thus, "$foo + $bar + $baz" will follow "$baz" all the way down the interpolation tree even if "$foo" immediately evaluates to "$$99.95" (which becomes "$99.95" and stops interpolation of "$foo"). Infinite-recursion loops will be detected as soon as any value is entered for the second time, and an exception will be thrown at that point. This has the added advantage of making the MAX_INTERPOL_DEPTH value completely unnecessary -- with my patch applied, MAX_INTERPOL_DEPTH can still be set, but it has no effect whatsoever. The recursion will continue down as deep as necessary as long as there are no loops. I've tested this code against all the scenarios I could come up with, and it's worked properly every time. This version of the patch makes no change to the "which section to look at" behavior: it only looks at sections named DEFAULT. Next up: a patch that changes this lookup order to the following: 1) Current section 2) Current section's DEFAULT 3) current = current.parent; repeat from 1 -- Robin Munn Rob...@gm... GPG key 0xD6497014 |
|
From: Robin M. <rob...@gm...> - 2006-06-08 12:57:23
Attachments:
change-search-behavior.patch
deepsearch.ini
|
Here's the patch to change the section search behavior. Instead of looking only at DEFAULT sections, it will look at the current section, then its DEFAULT, then move up to the parent section and repeat the process. This is a patch against a version of configobj.py that has the 3rd template interpolation patch (the one in my previous email to this thread) already applied. I've also attached the sample .ini file I used for testing, called "deepsearch.ini", so you can verify for yourself that the code works. Note that a couple of the documentation changes in this patch (particularly the ones where InterpolationDepthError was changed to InterpolationLoopError) should have gone into 3rd-template-interpolation.patch instead. This won't be a problem if both patches go into the SVN tree, but if you decide to reject the change-search-behavior.patch and keep the old "only search a few DEFAULT sections" behavior, then you'll need to cherrypick the relevant documentation patches out of this patch. Shouldn't be difficult, since this patch isn't all that large. I hope this makes it in; I'd really prefer the behavior that this patch implements. -- Robin Munn Rob...@gm... GPG key 0xD6497014 |
|
From: Nicola L. <ni...@te...> - 2006-12-09 17:39:10
|
Robin Munn wrote: > Here's the patch to change the section search behavior. Instead of > looking only at DEFAULT sections, it will look at the current section, > then its DEFAULT, then move up to the parent section and repeat the > process. > > This is a patch against a version of configobj.py that has the 3rd > template interpolation patch (the one in my previous email to this > thread) already applied. Both patches are now included in the SVN repo. Thanks again for them, Robin, and sorry for not having come round to them sooner. ConfigObj now supports both ConfigParser-style and Template-style interpolation, with a more consistent search strategy (see docs). > I've also attached the sample .ini file I used for testing, called > "deepsearch.ini", so you can verify for yourself that the code works. I added new tests to configobj_test.py based on this one and on the other .ini file. A new release containing these changes will have to wait for the further changes that are coming up in the pipeline. In the meantime, the SVN repo is reliable and tested. Please let us know any problems these changes may cause to your config files. -- Nicola Larosa - http://www.tekNico.net/ I have no interest in continuing down the J2EE path. I have seen Enterprise Java Beans with container managed persistence mappers that give me nightmares. I have seen Spring and Hibernate, and it seems like a terrible misuse and abuse of XML. I would like to drop Java like a hot rock at this point. -- Andrew Smith, June 2006 |
|
From: Michael F. <fuz...@vo...> - 2006-12-09 17:40:50
|
Wow ! Fuzzyman Nicola Larosa wrote: > Robin Munn wrote: >> Here's the patch to change the section search behavior. Instead of >> looking only at DEFAULT sections, it will look at the current section, >> then its DEFAULT, then move up to the parent section and repeat the >> process. >> >> This is a patch against a version of configobj.py that has the 3rd >> template interpolation patch (the one in my previous email to this >> thread) already applied. > > Both patches are now included in the SVN repo. Thanks again for them, > Robin, and sorry for not having come round to them sooner. > > > ConfigObj now supports both ConfigParser-style and Template-style > interpolation, with a more consistent search strategy (see docs). > > >> I've also attached the sample .ini file I used for testing, called >> "deepsearch.ini", so you can verify for yourself that the code works. > > I added new tests to configobj_test.py based on this one and on the other > .ini file. > > > A new release containing these changes will have to wait for the further > changes that are coming up in the pipeline. In the meantime, the SVN repo > is reliable and tested. Please let us know any problems these changes may > cause to your config files. > > -- No virus found in this outgoing message. Checked by AVG Free Edition. Version: 7.1.409 / Virus Database: 268.15.15/580 - Release Date: 08/12/2006 |
|
From: Michael F. <fuz...@vo...> - 2006-06-11 21:54:54
|
Robin Munn wrote: > On 6/6/06, Fuzzyman <fuz...@vo...> wrote: > >> Rick Kier wrote: >> >> >>> Hi Robin et all, >>> >>> Monday, June 05, 2006, Robin Munn wrote: >>> RM> There's one other behavior of the interpolation that was surprising to >>> RM> me: the fact that interpolated variables are *only* allowed to refer >>> RM> to values in the [DEFAULT] section. That means that I can't do: >>> >>> Agree 100% on this. >>> >>> We don't use interpolation due to the current behavior >>> >>> >>> >> I don't use string substitution at all, so I am willing to listen to the >> views of users on this. >> >> It could theoretically break behaviour for others. >> >> Anyone else got opinions ? >> >> Fuzzyman >> http://www.voidspace.org.uk/python/index.shtml >> > > After giving it some more thought, I'm starting to believe the best > interpolation behavior is going to be eager-interpolation, pulling > values from anywhere in the current section, its DEFAULTs, or its > parents and their DEFAULTs. So the algorithm looks like this: > > Robin. The only people who have spoken up are in favour of the new interpolation, and for interpolation using the current section as well as the default section. That means (unless a violent protest arises) that your changes will go into the next version of ConfigObj. I'm currently working on an update to Movable Python (and a few support modules) . This will probably take a couple of weeks, maybe a bit less. After that I will work on ConfigObj again and incorporate your changes. I will probably turn to you for assistance and clarification at that point. All the best, Fuzzyman http://www.voidspace.org.uk/python/index.shtml > When a string interpolation (in either "%(foo)s" or "$foo" syntax) is > seen, immediately try to interpret it as follows: > 1. Look for name "foo" in current section. If found, exit loop. > 2. Look for name "foo" in current section's DEFAULTs. If found, exit loop. > 3. Move "current section" reference to parent. If no parent, raise > NotFoundException. > 4. Repeat from step 1. > > > Advantages of this approach: > > 1. No need for special-case "Stop when a $$ is seen" code. > Special-case code usually makes me uncomfortable unless there's a darn > good reason for it, and this one smells like a workaround. > > 2. The scoping rules work like I would have expected them to work in > the first place: specifically-given values override default values, > and parent namespaces are consulted all the way up to the top. > > 3. No need for recursive evaluation, or any maximumum-nesting-depth > parameters. Since whatever $foo expands to has *already* been > evaluated (or else the "foo" name wouldn't exist), any interpolations > in the "foo" value have already been expanded. So we just replace > "$foo" with the value and we're done. > > > Disadvantages of this approach: > > 1. How do we round-trip? E.g., if we have "foo = 3" and "bar = $foo", > after we load the config file, the value of "bar" is 3, and so when we > write it out we'll get "foo = 3" and "bar = 3". Solution: if > interpolation happens, keep the value of the original string around > for use when we write out the config file. Question: where to keep > that original string? > > 2. Values modified after the config file is parsed won't propagate > their changes "upwards" to anything that includes them. If after > parsing the file, we set "foo" to 4, "bar" will still be 3. This may > not be the desired behavior for some people: they might want to change > the "path_root" value and have that change propagate throughout their > config structure. Solution: since we kept the original values around, > when "foo" changes, we can re-evaluate whatever depends on "foo". > (This would be a perfect place to use PyCells, once that project is > complete). Or we could simply re-evaluate the entire config structure > to be safe. > > Note: Some people may *not* want the value of "bar" to be changed when > "foo" is modified. We should make this an option somewhere -- perhaps > a "propagate_changes=False" keyword to a function call, or a > "propagate_changes" attribute in the ConfigObj instance. Haven't > thought about this one deeply yet. > > > Summary: I propose eager evaluation of interpolated values, with > lexical scoping for finding values. The original, non-interpolated > strings should be saved for the purposes of configfile round-tripping > and, optionally, post-evaluation propagation of changes to dependent > values (e.g., changing the value of "bar = $foo" when "foo" is > modified). > > |
|
From: Michael F. <fuz...@vo...> - 2006-06-08 12:34:21
|
Robin Munn wrote: > Here's the patch. This one implements a *proper* recursive, > lazy-loading interpolation approach, and no longer suffers from the > "stop when a $$ is seen" problem. If anything stops interpolation of > one key, the rest of the keys in the string will not have their > interpolation stopped. Thus, "$foo + $bar + $baz" will follow "$baz" > all the way down the interpolation tree even if "$foo" immediately > evaluates to "$$99.95" (which becomes "$99.95" and stops interpolation > of "$foo"). > Thanks for this Robin. I'm going to have to take some time to understand this and work out what is really going on. :-) It sounds good though, appreciated. Fuzzyman http://www.voidspace.org.uk/python/index.shtml > Infinite-recursion loops will be detected as soon as any value is > entered for the second time, and an exception will be thrown at that > point. This has the added advantage of making the MAX_INTERPOL_DEPTH > value completely unnecessary -- with my patch applied, > MAX_INTERPOL_DEPTH can still be set, but it has no effect whatsoever. > The recursion will continue down as deep as necessary as long as there > are no loops. > > I've tested this code against all the scenarios I could come up with, > and it's worked properly every time. > > This version of the patch makes no change to the "which section to > look at" behavior: it only looks at sections named DEFAULT. Next up: a > patch that changes this lookup order to the following: > > 1) Current section > 2) Current section's DEFAULT > 3) current = current.parent; repeat from 1 > > ------------------------------------------------------------------------ > > === docs/configobj.txt > ================================================================== > --- docs/configobj.txt (revision 20140) > +++ docs/configobj.txt (local) > @@ -786,13 +786,13 @@ > * create_empty > * file_error > > -interpolate > -~~~~~~~~~~~ > +interpolation > +~~~~~~~~~~~~~ > > ConfigObj can perform string interpolation in a *similar* way to > ``ConfigParser``. See the interpolation_ section for full details. > > -If ``interpolate`` is set to ``False``, then interpolation is *not* done when > +If ``interpolation`` is set to ``False``, then interpolation is *not* done when > you fetch values. > > stringify > @@ -1957,9 +1957,29 @@ > ============= > > ConfigObj allows string interpolation *similar* to the way ``ConfigParser`` > +or ``string.Template`` work. The value of the ``interpolation`` attribute > +determines which style of interpolation you want to use. Valid values are > +"ConfigParser" or "Template" (case-insensitive, so "configparser" and > +"template" will also work). For backwards compatibility reasons, the value > +``True`` is also a valid value for the ``interpolation`` attribute, and > +will select ``ConfigParser``-style interpolation. At some undetermined point > +in the future, that default *may* change to ``Template``-style interpolation. > > -You specify a value to be substituted by including ``%(name)s`` in the value. > +For ``ConfigParser``-style interpolation, you specify a value to be > +substituted by including ``%(name)s`` in the value. > > +For ``Template``-style interpolation, you specify a value to be substituted > +by including ``${name}`` in the value. Alternately, if 'name' is a valid > +Python identifier (i.e., is composed of nothing but alphanumeric characters, > +plus the underscore character), then the braces are optional and the value > +can be written as ``$name``. > + > +Note that ``ConfigParser``-style interpolation and ``Template``-style > +interpolation are mutually exclusive; you cannot have a configuration file > +that's a mix of one or the other. Pick one and stick to it. ``Template``-style > +interpolation is simpler to read and write by hand, and is recommended if > +you don't have a particular reason to use ``ConfigParser``-style. > + > Interpolation checks first the 'DEFAULT' sub-section of the current section to > see if ``name`` is the key to a value. ('name' is case sensitive). > > === pythonutils/configobj.py > ================================================================== > --- pythonutils/configobj.py (revision 20140) > +++ pythonutils/configobj.py (local) > @@ -102,6 +102,7 @@ > __all__ = ( > '__version__', > 'DEFAULT_INDENT_TYPE', > + 'DEFAULT_INTERPOLATION', > 'NUM_INDENT_SPACES', > 'MAX_INTERPOL_DEPTH', > 'ConfigObjError', > @@ -112,7 +113,7 @@ > 'ConfigObj', > 'SimpleVal', > 'InterpolationError', > - 'InterpolationDepthError', > + 'InterpolationLoopError', > 'MissingInterpolationOption', > 'RepeatSectionError', > 'UnreprError', > @@ -121,6 +122,7 @@ > 'flatten_errors', > ) > > +DEFAULT_INTERPOLATION = 'configparser' > DEFAULT_INDENT_TYPE = ' ' > NUM_INDENT_SPACES = 4 > MAX_INTERPOL_DEPTH = 10 > @@ -250,13 +252,13 @@ > class InterpolationError(ConfigObjError): > """Base class for the two interpolation errors.""" > > -class InterpolationDepthError(InterpolationError): > +class InterpolationLoopError(InterpolationError): > """Maximum interpolation depth exceeded in string interpolation.""" > > def __init__(self, option): > InterpolationError.__init__( > self, > - 'max interpolation depth exceeded in value "%s".' % option) > + 'interpolation loop detected in value "%s".' % option) > > class RepeatSectionError(ConfigObjError): > """ > @@ -276,11 +278,157 @@ > """An error parsing in unrepr mode.""" > > > +class InterpolationEngine(object): > + """ > + A helper class to help perform string interpolation. > + > + This class is an abstract base class; its descendants perform > + the actual work. > + """ > + > + # compiled regexp to use in self.interpolate() > + _KEYCRE = re.compile(r"%\(([^)]*)\)s") > + > + def __init__(self, section): > + # the Section instance that "owns" this engine > + self.section = section > + > + def interpolate(self, key, value): > + def recursive_interpolate(key, value, section, backtrail): > + """The function that does the actual work. > + > + ``value``: the string we're trying to interpolate. > + ``section``: the section in which that string was found > + ``backtrail``: a dict to keep track of where we've been, > + to detect and prevent infinite recursion loops > + > + This is similar to a depth-first-search algorithm. > + """ > + # Have we been here already? > + if backtrail.has_key((key, section.name)): > + # Yes - infinite loop detected > + raise InterpolationLoopError(key) > + # Place a marker on our backtrail so we won't come back here again > + backtrail[(key, section.name)] = 1 > + > + # Now start the actual work > + match = self._KEYCRE.search(value) > + while match: > + # The actual parsing of the match is implementation-dependent, > + # so delegate to our helper function > + k, v, s = self._parse_match(match) > + if k is None: > + # That's the signal that no further interpolation is needed > + replacement = v > + else: > + # Further interpolation may be needed to obtain final value > + replacement = recursive_interpolate(k, v, s, backtrail) > + # Replace the matched string with its final value > + start, end = match.span() > + value = ''.join((value[:start], replacement, value[end:])) > + new_search_start = start + len(replacement) > + # Pick up the next interpolation key, if any, for next time > + # through the while loop > + match = self._KEYCRE.search(value, new_search_start) > + > + # Now safe to come back here again; remove marker from backtrail > + del backtrail[(key, section.name)] > + > + return value > + > + # Back in interpolate(), all we have to do is kick off the recursive > + # function with appropriate starting values > + value = recursive_interpolate(key, value, self.section, {}) > + return value > + > + def _fetch(self, key): > + """Helper function to fetch values from owning section. > + > + Returns a 2-tuple: the value, and the section where it was found. > + """ > + # switch off interpolation before we try and fetch anything ! > + save_interp = self.section.main.interpolation > + self.section.main.interpolation = False > + # try the 'DEFAULT' member of owning section first > + current_section = self.section > + val = current_section.get('DEFAULT', {}).get(key) > + # try the 'DEFAULT' member of owner's parent section next > + if val is None: > + current_section = self.section.parent > + val = current_section.get('DEFAULT', {}).get(key) > + # last, try the 'DEFAULT' member of the main section > + if val is None: > + current_section = self.section.main > + val = current_section.get('DEFAULT', {}).get(key) > + # restore interpolation to previous value before returning > + self.section.main.interpolation = save_interp > + if val is None: > + raise MissingInterpolationOption(key) > + return val, current_section > + > + def _parse_match(self, match): > + """Implementation-dependent helper function. > + > + Will be passed a match object corresponding to the interpolation > + key we just found (e.g., "%(foo)s" or "$foo"). Should look up that > + key in the appropriate config file section (using the ``_fetch()`` > + helper function) and return a 3-tuple: (key, value, section) > + > + ``key`` is the name of the key we're looking for > + ``value`` is the value found for that key > + ``section`` is a reference to the section where it was found > + > + ``key`` and ``section`` should be None if no further > + interpolation should be performed on the resulting value > + (e.g., if we interpolated "$$" and returned "$"). > + """ > + raise NotImplementedError > + > + > +class ConfigParserInterpolation(InterpolationEngine): > + """Behaves like ConfigParser.""" > + _KEYCRE = re.compile(r"%\(([^)]*)\)s") > + > + def _parse_match(self, match): > + key = match.group(1) > + value, section = self._fetch(key) > + return key, value, section > + > + > +class TemplateInterpolation(InterpolationEngine): > + """Behaves like string.Template.""" > + _delimiter = '$' > + _KEYCRE = re.compile(r""" > + \$(?: > + (?P<escaped>\$) | # Two $ signs > + (?P<named>[_a-z][_a-z0-9]*) | # $name format > + {(?P<braced>[^}]*)} # ${name} format > + ) > + """, re.IGNORECASE | re.VERBOSE) > + > + def _parse_match(self, match): > + # Valid name (in or out of braces): fetch value from section > + key = match.group('named') or match.group('braced') > + if key is not None: > + value, section = self._fetch(key) > + return key, value, section > + # Escaped delimiter (e.g., $$): return single delimiter > + if match.group('escaped') is not None: > + # Return None for key and section to indicate it's time to stop > + return None, self._delimiter, None > + # Anything else: ignore completely, just return it unchanged > + return None, match.group(), None > + > +interpolation_engines = { > + 'configparser': ConfigParserInterpolation, > + 'template': TemplateInterpolation, > +} > + > class Section(dict): > """ > A dictionary-like object that represents a section in a config file. > > - It does string interpolation if the 'interpolate' attribute > + It does string interpolation if the 'interpolation' attribute > of the 'main' object is set to True. > > Interpolation is tried first from the 'DEFAULT' section of this object, > @@ -293,8 +441,6 @@ > Iteration follows the order: scalars, then sections. > """ > > - _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") > - > def __init__(self, parent, depth, main, indict=None, name=None): > """ > * parent is the section above > @@ -335,46 +481,33 @@ > for entry in indict: > self[entry] = indict[entry] > > - def _interpolate(self, value): > - """Nicked from ConfigParser.""" > - depth = MAX_INTERPOL_DEPTH > - # loop through this until it's done > - while depth: > - depth -= 1 > - if value.find("%(") != -1: > - value = self._KEYCRE.sub(self._interpolation_replace, value) > + def _interpolate(self, key, value): > + try: > + # do we already have an interpolation engine? > + engine = self._interpolation_engine > + except AttributeError: > + # not yet: first time running _interpolate(), so pick the engine > + name = self.main.interpolation > + if name == True: # note that "if name:" would be incorrect here > + # backwards-compatibility: interpolation=True means use default > + name = DEFAULT_INTERPOLATION > + name = name.lower() # so that "Template", "template", etc. all work > + klass = interpolation_engines.get(name, None) > + if klass is None: > + # invalid value for self.main.interpolation > + self.main.interpolation = False > + return value > else: > - break > - else: > - raise InterpolationDepthError(value) > - return value > + # save reference to engine so we don't have to do this again > + engine = self._interpolation_engine = klass(self) > + # let the engine do the actual work > + return engine.interpolate(key, value) > > - def _interpolation_replace(self, match): > - """ """ > - s = match.group(1) > - if s is None: > - return match.group() > - else: > - # switch off interpolation before we try and fetch anything ! > - self.main.interpolation = False > - # try the 'DEFAULT' member of *this section* first > - val = self.get('DEFAULT', {}).get(s) > - # try the 'DEFAULT' member of the *parent section* next > - if val is None: > - val = self.parent.get('DEFAULT', {}).get(s) > - # last, try the 'DEFAULT' member of the *main section* > - if val is None: > - val = self.main.get('DEFAULT', {}).get(s) > - self.main.interpolation = True > - if val is None: > - raise MissingInterpolationOption(s) > - return val > - > def __getitem__(self, key): > """Fetch the item and do string interpolation.""" > val = dict.__getitem__(self, key) > if self.main.interpolation and isinstance(val, StringTypes): > - return self._interpolate(val) > + return self._interpolate(key, val) > return val > > def __setitem__(self, key, value, unrepr=False): > @@ -471,7 +604,7 @@ > del self.inline_comments[key] > self.sections.remove(key) > if self.main.interpolation and isinstance(val, StringTypes): > - return self._interpolate(val) > + return self._interpolate(key, val) > return val > > def popitem(self): > > ------------------------------------------------------------------------ > > _______________________________________________ > Configobj-develop mailing list > Con...@li... > https://lists.sourceforge.net/lists/listinfo/configobj-develop > |