[Checkins] SVN: zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout. Merge branch 'master' into local/gotcha-annotate

Godefroid Chapelle gotcha at bubblenet.be
Sat Mar 28 21:16:16 EDT 2009


Log message for revision 98439:
  Merge branch 'master' into local/gotcha-annotate

Changed:
  U   zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.py
  U   zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.txt

-=-
Modified: zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.py
===================================================================
--- zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.py	2009-03-29 00:29:06 UTC (rev 98438)
+++ zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.py	2009-03-29 01:16:15 UTC (rev 98439)
@@ -29,7 +29,9 @@
 import ConfigParser
 import UserDict
 import glob
+import copy
 
+
 import pkg_resources
 import zc.buildout
 import zc.buildout.easy_install
@@ -65,7 +67,48 @@
     def __str__(self):
         return "The referenced section, %r, was not defined." % self[0]
 
-_buildout_default_options = {
+
+def _annotate_section(section, note):
+    for key in section:
+        section[key] = (section[key], note)
+    return section
+
+def _annotate(data, note):
+    for key in data:
+        data[key] = _annotate_section(data[key], note)
+    return data
+
+def _print_annotate(data):
+    sections = data.keys()
+    sections.sort()
+    print
+    print "Annotated sections"
+    print "="*len("Annotated sections")
+    for section in sections:
+        print
+        print '[%s]' % section
+        keys = data[section].keys()
+        keys.sort()
+        for key in keys:
+            value, files = data[section][key]
+            print "%s=%s" % (key, value)
+            for file in files.split():
+                print "    " + file
+    print
+    print
+
+def _unannotate_section(section):
+    for key in section:
+        value, note = section[key]
+        section[key] = value
+    return section
+
+def _unannotate(data):
+    for key in data:
+        data[key] = _unannotate_section(data[key])
+    return data
+
+_buildout_default_options = _annotate_section({
     'eggs-directory': 'eggs',
     'develop-eggs-directory': 'develop-eggs',
     'bin-directory': 'bin',
@@ -75,15 +118,17 @@
     'executable': sys.executable,
     'log-level': 'INFO',
     'log-format': '',
-    }
+    'annotate': 'false',
+    }, 'DEFAULT_VALUE')
 
+
 class Buildout(UserDict.DictMixin):
 
     def __init__(self, config_file, cloptions,
                  user_defaults=True, windows_restart=False, command=None):
 
         __doing__ = 'Initializing.'
-        
+
         self.__windows_restart = windows_restart
 
         # default options
@@ -101,13 +146,14 @@
                     # Sigh. this model of a buildout nstance
                     # with methods is breaking down :(
                     config_file = None
-                    data['buildout']['directory'] = '.'
+                    data['buildout']['directory'] = ('.', 'COMPUTED_VALUE')
                 else:
                     raise zc.buildout.UserError(
                         "Couldn't open %s" % config_file)
 
             if config_file:
-                data['buildout']['directory'] = os.path.dirname(config_file)
+                data['buildout']['directory'] = (os.path.dirname(config_file),
+                    'COMPUTED_VALUE')
         else:
             base = None
 
@@ -128,17 +174,20 @@
             options = data.get(section)
             if options is None:
                 options = data[section] = {}
-            options[option] = value
+            options[option] = value, "COMMAND_LINE_VALUE"
                 # The egg dire
 
-        self._raw = data
+        self._annotated = copy.deepcopy(data)
+        self._raw = _unannotate(data)
+        if data['buildout']['annotate'] == 'true':
+            _print_annotate(self._annotated)
         self._data = {}
         self._parts = []
         # provide some defaults before options are parsed
         # because while parsing options those attributes might be
         # used already (Gottfried Ganssauge)
         buildout_section = data.get('buildout')
-            
+
         # Try to make sure we have absolute paths for standard
         # directories. We do this before doing substitutions, in case
         # a one of these gets read by another section.  If any
@@ -155,13 +204,13 @@
 
         allow_hosts = buildout_section and buildout_section.get(
              'allow-hosts', '*').split('\n')
-        self._allow_hosts = tuple([host.strip() for host in allow_hosts 
+        self._allow_hosts = tuple([host.strip() for host in allow_hosts
                                    if host.strip() != ''])
 
         self._logger = logging.getLogger('zc.buildout')
         self.offline = False
         self.newest = True
-        
+
         ##################################################################
         ## WARNING!!!
         ## ALL ATTRIBUTES MUST HAVE REASONABLE DEFAULTS AT THIS POINT
@@ -173,9 +222,9 @@
         # now reinitialize
         links = options.get('find-links', '')
         self._links = links and links.split() or ()
-        
+
         allow_hosts = options.get('allow-hosts', '*').split('\n')
-        self._allow_hosts = tuple([host.strip() for host in allow_hosts 
+        self._allow_hosts = tuple([host.strip() for host in allow_hosts
                                    if host.strip() != ''])
 
         self._buildout_dir = options['directory']
@@ -216,7 +265,7 @@
             self._error('Invalid value for prefer-final option: %s',
                         prefer_final)
         zc.buildout.easy_install.prefer_final(prefer_final=='true')
-        
+
         use_dependency_links = options.get('use-dependency-links', 'true')
         if use_dependency_links not in ('true', 'false'):
             self._error('Invalid value for use-dependency-links option: %s',
@@ -243,7 +292,7 @@
             download_cache = os.path.join(download_cache, 'dist')
             if not os.path.isdir(download_cache):
                 os.mkdir(download_cache)
-                
+
             zc.buildout.easy_install.download_cache(download_cache)
 
         install_from_cache = options.get('install-from-cache')
@@ -335,7 +384,7 @@
         installed_develop_eggs = self._develop()
         installed_part_options['buildout']['installed_develop_eggs'
                                            ] = installed_develop_eggs
-        
+
         if installed_exists:
             self._update_installed(
                 installed_develop_eggs=installed_develop_eggs)
@@ -345,7 +394,7 @@
         conf_parts = conf_parts and conf_parts.split() or []
         installed_parts = installed_part_options['buildout']['parts']
         installed_parts = installed_parts and installed_parts.split() or []
-        
+
         if install_args:
             install_parts = install_args
             uninstall_missing = False
@@ -361,11 +410,11 @@
         if self._log_level < logging.DEBUG:
             sections = list(self)
             sections.sort()
-            print    
+            print
             print 'Configuration data:'
             for section in self._data:
                 _save_options(section, self[section], sys.stdout)
-            print    
+            print
 
 
         # compute new part recipe signatures
@@ -493,7 +542,7 @@
 
             if need_to_save_installed:
                 installed_part_options['buildout']['parts'] = (
-                    ' '.join(installed_parts))            
+                    ' '.join(installed_parts))
                 self._save_installed_options(installed_part_options)
                 installed_exists = True
             else:
@@ -581,7 +630,7 @@
                      if f not in old_files
                      ]))
                 raise
-                     
+
             else:
                 self._sanity_check_develop_eggs_files(dest, old_files)
                 return '\n'.join([os.path.join(dest, f)
@@ -628,7 +677,7 @@
                             value = value.replace(k, v)
                     options[option] = value
                 result[section] = Options(self, section, options)
-                        
+
             return result, True
         else:
             return ({'buildout': Options(self, 'buildout', {'parts': ''})},
@@ -656,8 +705,8 @@
                         # Sigh. This is the exectable used to run the buildout
                         # and, of course, it's in use. Leave it.
                         ):
-                        raise                    
-                
+                        raise
+
     def _install(self, part):
         options = self[part]
         recipe, entry = _recipe(options)
@@ -701,7 +750,7 @@
             buildout_handler.setFormatter(logging.Formatter('%(message)s'))
             self._logger.propagate = False
             self._logger.addHandler(buildout_handler)
-            
+
         handler.setFormatter(logging.Formatter(log_format))
         root_logger.addHandler(handler)
 
@@ -730,7 +779,7 @@
 
         if not self.newest:
             return
-        
+
         ws = zc.buildout.easy_install.install(
             [
             (spec + ' ' + self['buildout'].get(spec+'-version', '')).strip()
@@ -774,8 +823,8 @@
             if not __debug__:
                 args.insert(0, '-O')
             args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
-            os.execv(sys.executable, args)            
-        
+            os.execv(sys.executable, args)
+
         self._logger.info("Upgraded:\n  %s;\nrestarting.",
                           ",\n  ".join([("%s version %s"
                                        % (dist.project_name, dist.version)
@@ -784,7 +833,7 @@
                                       ]
                                      ),
                           )
-                
+
         # the new dist is different, so we've upgraded.
         # Update the scripts and return True
         zc.buildout.easy_install.scripts(
@@ -854,13 +903,13 @@
                 ))
             if is_jython:
                 arg_list = list()
-                
+
                 for a in args:
                     add_args.append(zc.buildout.easy_install._safe_arg(a))
-                
+
                 subprocess.Popen([zc.buildout.easy_install._safe_arg(sys.executable)] + list(tsetup) +
                                 arg_list).wait()
-            
+
             else:
                 os.spawnl(os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable), tsetup,
                         *[zc.buildout.easy_install._safe_arg(a)
@@ -886,7 +935,7 @@
         options = Options(self, section, data)
         self._data[section] = options
         options._initialize()
-        return options          
+        return options
 
     def __setitem__(self, key, value):
         raise NotImplementedError('__setitem__')
@@ -951,7 +1000,7 @@
     def _initialize(self):
         name = self.name
         __doing__ = 'Initializing section %s.', name
-        
+
         # force substitutions
         for k, v in self._raw.items():
             if '${' in v:
@@ -959,11 +1008,11 @@
 
         if self.name == 'buildout':
             return # buildout section can never be a part
-        
+
         recipe = self.get('recipe')
         if not recipe:
             return
-        
+
         reqs, entry = _recipe(self._data)
         buildout = self.buildout
         recipe_class = _install_and_load(reqs, 'zc.buildout', entry, buildout)
@@ -1035,7 +1084,7 @@
                         "The option name in substitution, %s,\n"
                         "has invalid characters."
                         % ref)
-                
+
             v = self.buildout[s[0]].get(s[1], None, seen)
             if v is None:
                 raise MissingOption("Referenced option does not exist:", *s)
@@ -1043,7 +1092,7 @@
         subs.append('')
 
         return ''.join([''.join(v) for v in zip(value[::2], subs)])
-        
+
     def __getitem__(self, key):
         try:
             return self._data[key]
@@ -1147,7 +1196,7 @@
     if value.endswith('\n\t'):
         value = value[:-2] + '%(__buildout_space_n__)s'
     print >>f, option, '=', value
-    
+
 def _save_options(section, options, f):
     print >>f, '[%s]' % section
     items = options.items()
@@ -1195,6 +1244,8 @@
             extended_by = options.pop('extended-by', extended_by)
         result[section] = options
 
+    result = _annotate(result, filename)
+
     if extends:
         extends = extends.split()
         extends.reverse()
@@ -1210,8 +1261,8 @@
 
     seen.pop()
     return result
-    
 
+
 ignore_directories = '.svn', 'CVS'
 def _dir_hash(dir):
     hash = md5()
@@ -1226,7 +1277,7 @@
         for name in filenames:
             hash.update(open(os.path.join(dirpath, name)).read())
     return hash.digest().encode('base64').strip()
-    
+
 def _dists_sig(dists):
     result = []
     for dist in dists:
@@ -1239,16 +1290,23 @@
 
 def _update_section(s1, s2):
     for k, v in s2.items():
+        v2, note2 = v
         if k.endswith('+'):
             key = k.rstrip(' +')
-            s2[key] = "\n".join(s1.get(key, "").split('\n') + s2[k].split('\n'))
+            v1, note1 = s1.get(key, ("", ""))
+            newnote = ' +'.join((note1, note2)).strip()
+            s2[key] = "\n".join((v1).split('\n') +
+                v2.split('\n')), newnote
             del s2[k]
         elif k.endswith('-'):
             key = k.rstrip(' -')
-            s2[key] = "\n".join([v for v in s1.get(key, "").split('\n')
-                                 if v not in s2[k].split('\n')])
+            v1, note1 = s1.get(key, ("", ""))
+            newnote = ' -'.join((note1, note2)).strip()
+            s2[key] = ("\n".join(
+                [v for v in v1.split('\n')
+                   if v not in v2.split('\n')]), newnote)
             del s2[k]
-                
+
     s1.update(s2)
     return s1
 
@@ -1278,7 +1336,7 @@
         if d:
             doing.append(d)
         tb = tb.tb_next
-        
+
     if doing:
         sys.stderr.write('While:\n')
         for d in doing:
@@ -1335,13 +1393,13 @@
      Don't read user defaults.
 
   -o
-  
-    Run in off-line mode.  This is equivalent to the assignment 
+
+    Run in off-line mode.  This is equivalent to the assignment
     buildout:offline=true.
 
   -O
 
-    Run in non-off-line mode.  This is equivalent to the assignment 
+    Run in non-off-line mode.  This is equivalent to the assignment
     buildout:offline=false.  This is the default buildout mode.  The
     -O option would normally be used to override a true offline
     setting in a configuration file.
@@ -1355,10 +1413,10 @@
 
   -N
 
-    Run in non-newest mode.  This is equivalent to the assignment 
+    Run in non-newest mode.  This is equivalent to the assignment
     buildout:newest=false.  With this setting, buildout will not seek
     new distributions if installed distributions satisfy it's
-    requirements. 
+    requirements.
 
   -D
 
@@ -1366,6 +1424,11 @@
     will be started. This is especially useful for debuging recipe
     problems.
 
+  -A
+
+    Display annotated sections. Each key-value pair is displayed along
+    with its value origin.
+
 Assignments are of the form: section:option=value and are used to
 provide configuration options that override those given in the
 configuration file.  For example, to run the buildout in offline mode,
@@ -1408,7 +1471,7 @@
 
     The script can be given either as a script path or a path to a
     directory containing a setup.py script.
-    
+
 """
 def _help():
     print _usage
@@ -1428,7 +1491,7 @@
         if args[0][0] == '-':
             op = orig_op = args.pop(0)
             op = op[1:]
-            while op and op[0] in 'vqhWUoOnND':
+            while op and op[0] in 'vqhWUoOnNDA':
                 if op[0] == 'v':
                     verbosity += 10
                 elif op[0] == 'q':
@@ -1447,10 +1510,12 @@
                     options.append(('buildout', 'newest', 'false'))
                 elif op[0] == 'D':
                     debug = True
+                elif op[0] == 'A':
+                    options.append(('buildout', 'annotate', 'true'))
                 else:
                     _help()
                 op = op[1:]
-                
+
             if op[:1] in  ('c', 't'):
                 op_ = op[:1]
                 op = op[1:]
@@ -1527,10 +1592,10 @@
                     sys.stderr.write(_internal_error_template)
                     traceback.print_exception(*exc_info)
                     sys.exit(1)
-    
-            
+
+
     finally:
-            logging.shutdown()
+        logging.shutdown()
 
 if sys.version_info[:2] < (2, 4):
     def reversed(iterable):

Modified: zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.txt	2009-03-29 00:29:06 UTC (rev 98438)
+++ zc.buildout/branches/gotcha-annotate/src/zc/buildout/buildout.txt	2009-03-29 01:16:15 UTC (rev 98439)
@@ -707,6 +707,57 @@
 keep section and option names simple, sticking to alphanumeric
 characters, hyphens, and periods.
 
+Annotated sections
+------------------
+
+When used with the -A option, buildout displays annotated sections. 
+All sections are displayed, sorted alphabetically. For each section,
+all key-value pairs are displayed, sorted alphabetically, along with 
+the origin of the value (file name or COMPUTED_VALUE, DEFAULT_VALUE, 
+COMMAND_LINE_VALUE).
+
+    >>> print system(buildout+ ' -A'), # doctest: +ELLIPSIS
+    <BLANKLINE>
+    Annotated sections
+    ==================
+    <BLANKLINE>
+    [buildout]
+    annotate=true
+        COMMAND_LINE_VALUE
+    bin-directory=bin
+        DEFAULT_VALUE
+    develop=recipes
+        .../_TEST_/sample-buildout/buildout.cfg
+    develop-eggs-directory=develop-eggs
+        DEFAULT_VALUE
+    directory=.../_TEST_/sample-buildout
+        COMPUTED_VALUE
+    eggs-directory=eggs
+        DEFAULT_VALUE
+    executable=...
+        DEFAULT_VALUE
+    installed=.installed.cfg
+        DEFAULT_VALUE
+    log-format=
+        DEFAULT_VALUE
+    log-level=INFO
+        DEFAULT_VALUE
+    parts=data-dir
+        .../_TEST_/sample-buildout/buildout.cfg
+    parts-directory=parts
+        DEFAULT_VALUE
+    python=buildout
+        DEFAULT_VALUE
+    <BLANKLINE>
+    [data-dir]
+    path=foo bins
+        .../_TEST_/sample-buildout/buildout.cfg
+    recipe=recipes:mkdir
+        .../_TEST_/sample-buildout/buildout.cfg
+    <BLANKLINE>
+    <BLANKLINE>
+    ...
+
 Variable substitutions
 ----------------------
 
@@ -1025,6 +1076,46 @@
     ['a1 a2/na3 a4/na5', 'b1 b2 b3 b4', 'c1 c2/nc3 c4 c5', 'h1 h2']
     Develop: '/sample-buildout/demo'
 
+Annotated sections output shows which files are responsible for which
+operations.
+
+    >>> print system(os.path.join('bin', 'buildout') + ' -A'), # doctest: +ELLIPSIS
+    <BLANKLINE>
+    Annotated sections
+    ==================
+    ...
+    <BLANKLINE>
+    [part1]
+    option=a1 a2
+    a3 a4
+    a5
+        .../_TEST_/sample-buildout/base.cfg
+        +.../_TEST_/sample-buildout/extension1.cfg
+        +.../_TEST_/sample-buildout/extension2.cfg
+    recipe=
+        .../_TEST_/sample-buildout/base.cfg
+    <BLANKLINE>
+    [part2]
+    option=b1 b2 b3 b4
+        .../_TEST_/sample-buildout/base.cfg
+        -.../_TEST_/sample-buildout/extension1.cfg
+        -.../_TEST_/sample-buildout/extension2.cfg
+    recipe=
+        .../_TEST_/sample-buildout/base.cfg
+    <BLANKLINE>
+    [part3]
+    option=c1 c2
+    c3 c4 c5
+        .../_TEST_/sample-buildout/base.cfg
+        +.../_TEST_/sample-buildout/extension1.cfg
+    recipe=
+        .../_TEST_/sample-buildout/base.cfg
+    <BLANKLINE>
+    [part4]
+    option=h1 h2
+        .../_TEST_/sample-buildout/extension1.cfg
+    ...
+
 Cleanup.
 
     >>> os.remove(os.path.join(sample_buildout, 'base.cfg'))
@@ -1627,6 +1718,12 @@
     new distributions if installed distributions satisfy it's
     requirements.
 
+-A
+    Display annotated sections. All sections are displayed, sorted
+    alphabetically. For each section, all key-value pairs are displayed,
+    sorted alphabetically, along with the origin of the value (file name or 
+    COMPUTED_VALUE, DEFAULT_VALUE, COMMAND_LINE_VALUE).
+
 Assignments are of the form::
 
   section_name:option_name=value
@@ -2029,6 +2126,7 @@
     <BLANKLINE>
     Configuration data:
     [buildout]
+    annotate = false
     bin-directory = /sample-buildout/bin
     develop-eggs-directory = /sample-buildout/develop-eggs
     directory = /sample-buildout



More information about the Checkins mailing list