############################################################################## # # Copyright (c) 2004 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """STX Configuration Documentation Renderer Usage: stxdocs.py [options] Options: -h / --help Print this message and exit. -f Specifies the root ZCML meta directives file, relative to the current location. All included files will be considered as well -o Specifies a directory, relative to the current location in which the documentation is stored. Note that this tool will create sub-directories with files in them. $Id: stxdocs.py 69583 2006-08-17 07:57:48Z baijum $ """ import sys, os, getopt import zope.configuration from zope.schema import getFieldsInOrder from zope.configuration import config, xmlconfig from zope.configuration.docutils import wrap, makeDocStructures def usage(code, msg=''): # Python 2.1 required print >> sys.stderr, __doc__ if msg: print >> sys.stderr, msg sys.exit(code) def _directiveDocs(name, schema, handler, info, indent_offset=0): """Generate the documentation for one directive.""" # Write out the name of the directive text = ' '*indent_offset text += '%s\n\n' %name # Specify the file and location it has been declared if isinstance(info, xmlconfig.ParserInfo): # We do not want to specify the whole path; starting at the 'zope' # package is enough. base_dir = os.path.dirname(os.path.dirname( zope.configuration.__file__))[:-4] file = info.file.replace(base_dir, '') info_text = 'File %s, lines %i - %i.' %(file, info.line, info.eline) text += wrap(info_text, 78, indent_offset+2) elif isinstance(info, (str, unicode)) and info: text += wrap(info, 78, indent_offset+2) # Insert Handler information if handler is not None: handler_path = handler.__module__ + '.' + handler.__name__ text += wrap('Handler: %s' %handler_path, 78, indent_offset+2) # Use the schema documentation string as main documentation text for the # directive. text += wrap(schema.getDoc(), 78, indent_offset+2) text += ' '*indent_offset + ' Attributes\n\n' # Create directive attribute documentation for name, field in getFieldsInOrder(schema): name = name.strip('_') if field.required: opt = 'required' else: opt = 'optional, default=%s' %repr(field.default) text += ' '*indent_offset text += ' %s -- %s (%s)\n\n' %(name, field.__class__.__name__, opt) text += wrap(field.title, 78, indent_offset+6) text += wrap(field.description, 78, indent_offset+6) return text def _subDirectiveDocs(subdirs, namespace, name): """Appends a list of sub-directives and their full specification.""" if subdirs.has_key((namespace, name)): text = '\n Subdirectives\n\n' sub_dirs = [] # Simply walk through all sub-directives here. subs = subdirs[(namespace, name)] for sd_ns, sd_name, sd_schema, sd_handler, sd_info in subs: sub_dirs.append(_directiveDocs( sd_name, sd_schema, sd_handler, sd_info, 4)) return text + '\n\n'.join(sub_dirs) return '' def makedocs(target_dir, zcml_file): """Generate the documentation tree. All we need for this is a starting ZCML file and a directory in which to put the documentation. """ context = xmlconfig.file(zcml_file, execute=False) namespaces, subdirs = makeDocStructures(context) for namespace, directives in namespaces.items(): ns_dir = os.path.join(target_dir, namespace.split('/')[-1]) # Create a directory for the namespace, if necessary if not os.path.exists(ns_dir): os.mkdir(ns_dir) # Create a file for each directive for name, (schema, handler, info) in directives.items(): dir_file = os.path.join(ns_dir, name+'.stx') text = _directiveDocs(name, schema, handler, info) text += _subDirectiveDocs(subdirs, namespace, name) open(dir_file, 'w').write(text) def _makeabs(path): """Make an absolute path from the possibly relative path.""" if not path == os.path.abspath(path): cwd = os.getcwd() # This is for symlinks. if os.environ.has_key('PWD'): cwd = os.environ['PWD'] path = os.path.normpath(os.path.join(cwd, path)) return path def main(argv=sys.argv): try: opts, args = getopt.getopt( sys.argv[1:], 'h:f:o:', ['help']) except getopt.error, msg: usage(1, msg) zcml_file = None output_dir = None for opt, arg in opts: if opt in ('-h', '--help'): usage(0) elif opt in ('-o', ): output_dir = arg elif opt in ('-f', ): zcml_file = _makeabs(arg) if not os.path.exists(zcml_file): usage(1, 'The specified zcml file does not exist.') if zcml_file is None or output_dir is None: usage(0, "Both, the '-f' and '-o' option are required") # Generate the docs makedocs(output_dir, zcml_file) if __name__ == '__main__': main()