|
From: Fuzzyman <fuz...@vo...> - 2006-03-17 10:57:34
|
Hello Louis, Can you explain to me what this actually does (sorry for my ignorance). You want to create a default config file from a configspec. You want to copy the comments from the configspec *into* the config file when this is done ? You have attempted to do this with walk, but walk *doesn't* honour the order of the configspec, so you have implemented an alternative. Does that cover it all ? If so then I need to fix ``walk`` so that it *does* honour the order. Could I build your functionality into ConfigObj with an optional ``copy_comments`` option to the validate method ? You could then create your default config file by doing : from validate import Validator from configobj import ConfigObj vdt = Validator() cfg = ConfigObj(configspec=filename) cfg.validate(vdt, copy_comments=True) cfg.write() Is there any functionality you are suggesting that I have missed ? Nicola (other users/developers), are you happy for this to be built into ConfigObj, or would it be better as a separate function ? ``__many__`` should be ignored in the configspec - it is only used to validate sections that exist (of which there may be none, one or more). This is what ``validate`` already does. String interpolation should be off, leaving the substitution to be done when values are fetched. Fuzzyman http://www.voidspace.org.uk/python/index.shtml Nicola Larosa wrote: > Hi Louis, sorry for the late answer, this email somehow escaped my > attention for a while. > > Thanks a lot for your contribution, it seems interesting. Michael? > > > Louis Cordier wrote: > >> Hi guys, >> >> Here is a piece of code to generate a 'default' config file >> from the configspec. Basically it copies the comments and >> default values while retaining the sequential order of the >> configspec file. I can think of a few cases where one would >> like to generate a default configfile with proper comments >> for a user (if his/hers' are lost), while keeping them out >> of the configspec file. The comments can list all the valid >> values. >> >> For example how some X servers auto generate a default config >> file. >> >> Anyway this isn't fully complete. I am not too sure how to >> deal with [__many__] or string substitution yet. >> >> I also experimented (ignoring the comments) with using .walk(): >> >> def walker(s, k): >> s.defaults = [] >> >> validator = Validator() >> d = ConfigObj(options={'configspec': 'config.spec'}) >> d.validate(validator) >> d.walk(walker) >> d.filename = 'config.ini' >> d.write() >> >> This for some reason loses the internal order of 'config.spec'. >> >> I think there is probably a few other people that might like this >> functionality. You guys are welcome to add it to configobj.py >> or put it on the webpage. >> >> I suspect this would be a nice way of doing it: >> >> c = ConfigObj(options={'configspec': 'config.spec', >> 'include_comments': True, >> 'retain_order': True, >> 'indent_type': ' '}) >> c.validate(validator) >> c.filename = 'config.ini' >> c.write() >> >> Regards, Louis >> >> >> ------------------------------------------------------------------------ >> >> #!/usr/bin/env python >> >> # >> # author : Louis Cordier <lco...@gm...> >> # description : Testing code for generating a default config file. >> # synopsis : >> # notes : >> # modified : $Id$ >> # >> >> from configobj import ConfigObj, ConfigObjError >> from validate import Validator >> from datetime import datetime >> >> def create_config(configspec): >> """ Create a default config file based on the configspec file. >> >> Load the configspec file as if it was a config file, get the order >> sequence of the sections, keys-value pairs and their comments. >> All configspec values are then interpeted as strings to be ignored. >> Walk the configspec and create a new config file with the default >> values and comments of the configspec. >> >> @note: This is a special case, [__many__] are not yet supported. >> >> @note: ConfigObj.walk() will only work on sections to copy the >> comments, not the complete config object. >> We could transverse the parent()-tree, this feels ugly though. >> >> @note: We could recursively empty all the defaults members and copy >> the comments, but then we loose the configspec sequence though. >> """ >> >> def copy_section(spec_section, source_section, target_section): >> """ Recursive copy sections. >> >> Copy default values from source_section to target_section >> and comments from spec_section to target_section. >> Follow the sequence as specified in spec_section. >> """ >> # Deal with the root section, the config object itself. >> if hasattr(source_section,'initial_comment'): >> target_section.initial_comment = spec_section.initial_comment >> >> # Deal with the root section, the config object itself. >> if hasattr(source_section,'final_comment'): >> target_section.final_comment = spec_section.final_comment >> >> target_section.inline_comments = spec_section.inline_comments >> target_section.comments = spec_section.comments >> >> for key in spec_section.keys(): >> if key in spec_section.sections: >> target_section[key] = {} >> copy_section(spec_section[key], source_section[key], target_section[key]) >> else: >> target_section[key] = source_section[key] >> >> >> try: >> spec = ConfigObj(infile=configspec) >> >> except (IOError), e: >> # The configspec file needs to exist for this to work. >> pass >> >> # Let this function raise configobj.ConfigObjError, >> # in case of a broken configspec. >> >> else: >> try: >> source = ConfigObj(options={'configspec': configspec}) >> >> except (IOError), e: >> # The configspec file needs to exist for this to work. >> pass >> >> # Broken configspec, should have failed earlier already. >> >> else: >> # Now get all the default values. >> # This can only fail if the configspec is broken. >> validator = Validator() >> valid = source.validate(validator) >> >> if valid == True: >> # Create a new config object to be populated. >> target = ConfigObj(options={'configspec': configspec, >> 'indent_type': ' '}) >> >> copy_section(spec, source, target) >> return target >> else: >> raise ConfigObjError, "Bad configspec, '%s'" % (configspec) >> >> # It should ideally never get here. >> return None >> >> >> if __name__ == '__main__': >> >> # Maybe put configspec in the code itself, if config.ini doesn't exist >> # it could write a default file with comments. >> try: >> default_config = create_config('config.spec') >> except (ConfigObjError), e: >> print 'Ooops', e >> else: >> default_config.filename = 'config.ini' >> default_config.initial_comment.insert(0, 'Automatically generated from %s on %sZ\n' % ('config.spec', datetime.utcnow().isoformat())) >> default_config.write() >> >> >> >> ------------------------------------------------------------------------ >> >> # >> # Main config file... >> # >> >> # >> # Server setup >> # >> [server] >> active = boolean(default=True) >> name = string(default=Michael) >> age = float(default=0.0) >> sex = option(m, f, default=m) >> >> >> # >> # Database setup >> # >> [database] >> database = string(default=hydra) >> host = string(default=nostromo.intranet) >> user = string(default=hydra) >> password = string(default=hydra) >> >> # >> # Level 1 Comment >> # >> [[level1]] >> tl1=integer(default=1) >> >> # >> # Edit only if you know what you are doing. >> tl2=integer(default=2) # tl2 inline >> >> [[[test]]] >> test1=integer(default=1) >> test2=integer(default=2) >> >> [[[[sub_test]]]] >> # sub1 >> sub1=integer(default=1) #sub1-inline >> # sub2 >> sub2=integer(default=2) #sub2-inline >> >> # >> # Level 1 footer. >> # >> >> > > > |
|
From: Louis C. <lco...@po...> - 2006-03-17 11:47:39
|
On Fri, 17 Mar 2006, Fuzzyman wrote:
> Can you explain to me what this actually does (sorry for my ignorance).
>
> You want to create a default config file from a configspec. You want to copy
> the comments from the configspec *into* the config file when this is done ?
Yes, retaining the layout of the configspec.
> You have attempted to do this with walk, but walk *doesn't* honour the order
> of the configspec, so you have implemented an alternative.
My main aim wasn't to re-implement walk. In some case I have seen
walk honour the order and in others not. (not too sure why)
I simply tried to cheat write() into writing a config-file that the
validator populated with default values. I used walk to recursively
empty each section's defaults member (section.defaults = []).
I noticed that write didn't write those entries that was in .defaults
so I removed them all. ;)
Here is an example where walk() doesn't honour my configspec's order.
For my needed end result, I don't really need walk() to do it in
order thought.
>>> configspec = '''
... [test]
... c = string(default='c')
... b = string(default='b')
... a = string(default='a')
... z = string(default='z')
... '''
>>> c = ConfigObj(options={'configspec': configspec.split('\n')})
>>> from validate import Validator
>>> validator = Validator()
>>> c.validate(validator)
True
>>> c
{'test': {'a': 'a', 'c': 'c', 'b': 'b', 'z': 'z'}}
>>> def p(s, k):
... print s.name, k
...
#
# The order is incorrect.
#
>>> c.walk(p)
test a
test c
test b
test z
{'test': {'a': None, 'c': None, 'b': None, 'z': None}}
However, if I were to use walk() to create a new ConfigObj() and
write that to file, it will write the entries in the file in the
order they we added to the config ConfigObj().
> Does that cover it all ?
Generally, yes.
> If so then I need to fix ``walk`` so that it *does* honour the order. Could I
> build your functionality into ConfigObj with an optional ``copy_comments``
> option to the validate method ?
Nice and clean, I like it.
> You could then create your default config file by doing :
>
> from validate import Validator
> from configobj import ConfigObj
>
> vdt = Validator()
> cfg = ConfigObj(configspec=filename)
> cfg.validate(vdt, copy_comments=True)
> cfg.write()
Beautiful.
> Is there any functionality you are suggesting that I have missed ?
Nope, you seems to cover it all.
Thanks, Louis.
--
Louis Cordier <lco...@po...> cell: +27721472305
Point45 Entertainment (Pty) Ltd. http://www.point45.org
|
|
From: Fuzzyman <fuz...@vo...> - 2006-03-17 12:04:27
|
Louis Cordier wrote: > > On Fri, 17 Mar 2006, Fuzzyman wrote: >> Can you explain to me what this actually does (sorry for my ignorance). >> >> You want to create a default config file from a configspec. You want >> to copy the comments from the configspec *into* the config file when >> this is done ? > > Yes, retaining the layout of the configspec. > >> You have attempted to do this with walk, but walk *doesn't* honour >> the order of the configspec, so you have implemented an alternative. > > My main aim wasn't to re-implement walk. In some case I have seen > walk honour the order and in others not. (not too sure why) > Right - I think that where ``walk`` doesn't honour order that is a bug and I will fix it. Thanks for your example illustrating this. > I simply tried to cheat write() into writing a config-file that the > validator populated with default values. I used walk to recursively > empty each section's defaults member (section.defaults = []). > I noticed that write didn't write those entries that was in .defaults > so I removed them all. ;) > Ahh... I forgot that normally ``write`` doesn't write out unchanged default values. Instead of a ``copy_comments`` option we need a copy mode. How about an optional ``copy`` keyword to ``validate`` ? If this is set then comments are copied and none of the values supplied from the configspec are marked as default - in this case ``write`` should behave as expected. > [snip..] All the best, Fuzzyman http://www.voidspace.org.uk/python/index.shtml |
|
From: Fuzzyman <fuz...@vo...> - 2006-03-17 12:49:24
|
Louis Cordier wrote:
> [snip..]
>
> Here is an example where walk() doesn't honour my configspec's order.
> For my needed end result, I don't really need walk() to do it in
> order thought.
>
>>>> configspec = '''
> ... [test]
> ... c = string(default='c')
> ... b = string(default='b')
> ... a = string(default='a')
> ... z = string(default='z')
> ... '''
>
>>>> c = ConfigObj(options={'configspec': configspec.split('\n')})
>>>> from validate import Validator
>>>> validator = Validator()
>>>> c.validate(validator)
> True
>>>> c
> {'test': {'a': 'a', 'c': 'c', 'b': 'b', 'z': 'z'}}
Note that here the order is *already* incorrect. So validate doesn't
honour the order of the configspec. This is because the configspec is
stored in a dictionary attached to the section - looks like I need to
*also* store the order. I don't think I'll make that a public attribute
though.
As far as I can tell from the code, ``walk`` *ought* to honour the
order. I think this is why you are seeing it sometimes honour and
sometimes not.
Fuzzyman
http://www.voidspace.org.uk/python/configobj.html
|
|
From: Nicola L. <ni...@te...> - 2006-03-17 12:02:40
|
> Nicola (other users/developers), are you happy for this to be built into > ConfigObj, or would it be better as a separate function ? No objections to having it built-in. -- Nicola Larosa - http://www.tekNico.net/ One of the basic tenets of the Python language has been that code should be simple and clear to express and to read, and Ruby has followed this idea, although not as far as Python has because of the inherited Perlisms. -- Bruce Eckel, December 2005 |