def _unreachable_name_for(val_doc, docindex): assert isinstance(val_doc, ValueDoc) # [xx] (when) does this help? if (isinstance(val_doc, ModuleDoc) and len(val_doc.canonical_name) == 1 and val_doc.package is None): for root_val in docindex.root: if root_val.canonical_name == val_doc.canonical_name: if root_val != val_doc: log.error("Name conflict: %r vs %r" % (val_doc, root_val)) break else: return val_doc.canonical_name, -1000 # Assign it an 'unreachable' name: if (val_doc.pyval is not UNKNOWN and hasattr(val_doc.pyval, '__name__')): try: name = DottedName(DottedName.UNREACHABLE, val_doc.pyval.__name__) except DottedName.InvalidDottedName: name = DottedName(DottedName.UNREACHABLE) else: name = DottedName(DottedName.UNREACHABLE) # Uniquify the name. if name in _unreachable_names: n = 2 while DottedName('%s-%s' % (name, n)) in _unreachable_names: n += 1 name = DottedName('%s-%s' % (name, n)) _unreachable_names.add(name) return name, -10000
def _write(self, write_func, directory, filename, *args): # Display our progress. self._files_written += 1 log.progress(self._files_written/self._num_files, filename) path = os.path.join(directory, filename) if self._encoding == 'utf8': f = codecs.open(path, 'w', 'utf-8') write_func(f.write, *args) f.close() else: result = [] write_func(result.append, *args) s = u''.join(result) try: s = s.encode(self._encoding) except UnicodeError: log.error("Output could not be represented with the " "given encoding (%r). Unencodable characters " "will be displayed as '?'. It is recommended " "that you use a different output encoding (utf8, " "if it's supported by latex on your system).") s = s.encode(self._encoding, 'replace') f = open(path, 'w') f.write(s) f.close()
def _write(self, write_func, directory, filename, *args): # Display our progress. self._files_written += 1 log.progress(self._files_written / self._num_files, filename) path = os.path.join(directory, filename) if self._encoding == 'utf-8': f = codecs.open(path, 'w', 'utf-8') write_func(f.write, *args) f.close() else: result = [] write_func(result.append, *args) s = u''.join(result) try: s = s.encode(self._encoding) except UnicodeError: log.error("Output could not be represented with the " "given encoding (%r). Unencodable characters " "will be displayed as '?'. It is recommended " "that you use a different output encoding (utf-8, " "if it's supported by latex on your system)." % self._encoding) s = s.encode(self._encoding, 'replace') f = open(path, 'w') f.write(s) f.close()
def add_module(self, name, check=0): from epydoc.util import is_package_dir, is_pyname, is_module_file from epydoc.docintrospecter import get_value_from_name from epydoc.docintrospecter import get_value_from_filename if (os.path.isfile(name) or is_package_dir(name) or is_pyname(name)): # Check that it's a good module, if requested. if check: try: if is_module_file(name) or is_package_dir(name): get_value_from_filename(name) elif os.path.isfile(name): get_value_from_scriptname(name) else: get_value_from_name(name) except ImportError as e: log.error(e) self._update_messages() self._root.bell() return # Add the module to the list of modules. self._module_list.insert('end', name) self._module_list.yview('end') else: log.error("Couldn't find %r" % name) self._update_messages() self._root.bell()
def _unreachable_name_for(val_doc, docindex): assert isinstance(val_doc, ValueDoc) # [xx] (when) does this help? if (isinstance(val_doc, ModuleDoc) and len(val_doc.canonical_name)==1 and val_doc.package is None): for root_val in docindex.root: if root_val.canonical_name == val_doc.canonical_name: if root_val != val_doc: log.error("Name conflict: %r vs %r" % (val_doc, root_val)) break else: return val_doc.canonical_name, -1000 # Assign it an 'unreachable' name: if (val_doc.pyval is not UNKNOWN and hasattr(val_doc.pyval, '__name__')): try: name = DottedName(DottedName.UNREACHABLE, val_doc.pyval.__name__) except DottedName.InvalidDottedName: name = DottedName(DottedName.UNREACHABLE) else: name = DottedName(DottedName.UNREACHABLE) # Uniquify the name. if name in _unreachable_names: n = 2 while DottedName('%s-%s' % (name,n)) in _unreachable_names: n += 1 name = DottedName('%s-%s' % (name,n)) _unreachable_names.add(name) return name, -10000
def open(self, prjfile): from epydoc.docwriter.html_css import STYLESHEETS self._filename = prjfile try: opts = load(open(prjfile, "r")) modnames = list(opts.get("modules", [])) modnames.sort() self._module_list.delete(0, "end") for name in modnames: self.add_module(name) self._module_entry.delete(0, "end") self._name_entry.delete(0, "end") if opts.get("prj_name"): self._name_entry.insert(0, opts["prj_name"]) self._url_entry.delete(0, "end") if opts.get("prj_url"): self._url_entry.insert(0, opts["prj_url"]) self._docformat_var.set(opts.get("docformat", "epytext")) self._inheritance_var.set(opts.get("inheritance", "grouped")) self._introspect_or_parse_var.set(opts.get("introspect_or_parse", "both")) self._help_entry.delete(0, "end") if opts.get("help") is None: self._help_var.set("default") else: self._help_var.set("-other-") self._help_entry.insert(0, opts.get("help")) self._out_entry.delete(0, "end") self._out_entry.insert(0, opts.get("target", "html")) self._frames_var.set(opts.get("frames", 1)) self._private_var.set(opts.get("private", 1)) self._imports_var.set(opts.get("show_imports", 0)) self._css_entry.delete(0, "end") if opts.get("css", "default") in STYLESHEETS.keys(): self._css_var.set(opts.get("css", "default")) else: self._css_var.set("-other-") self._css_entry.insert(0, opts.get("css", "default")) # if opts.get('private_css', 'default') in STYLESHEETS.keys(): # self._private_css_var.set(opts.get('private_css', 'default')) # else: # self._private_css_var.set('-other-') # self._css_entry.insert(0, opts.get('private_css', 'default')) except Exception, e: log.error("Error opening %s: %s" % (prjfile, e)) self._root.bell()
def open(self, prjfile): from epydoc.docwriter.html_css import STYLESHEETS self._filename = prjfile try: opts = load(open(prjfile, 'r')) modnames = list(opts.get('modules', [])) modnames.sort() self._module_list.delete(0, 'end') for name in modnames: self.add_module(name) self._module_entry.delete(0, 'end') self._name_entry.delete(0, 'end') if opts.get('prj_name'): self._name_entry.insert(0, opts['prj_name']) self._url_entry.delete(0, 'end') if opts.get('prj_url'): self._url_entry.insert(0, opts['prj_url']) self._docformat_var.set(opts.get('docformat', 'epytext')) self._inheritance_var.set(opts.get('inheritance', 'grouped')) self._introspect_or_parse_var.set( opts.get('introspect_or_parse', 'both')) self._help_entry.delete(0, 'end') if opts.get('help') is None: self._help_var.set('default') else: self._help_var.set('-other-') self._help_entry.insert(0, opts.get('help')) self._out_entry.delete(0, 'end') self._out_entry.insert(0, opts.get('target', 'html')) self._frames_var.set(opts.get('frames', 1)) self._private_var.set(opts.get('private', 1)) self._imports_var.set(opts.get('show_imports', 0)) self._css_entry.delete(0, 'end') if opts.get('css', 'default') in list(STYLESHEETS.keys()): self._css_var.set(opts.get('css', 'default')) else: self._css_var.set('-other-') self._css_entry.insert(0, opts.get('css', 'default')) #if opts.get('private_css', 'default') in STYLESHEETS.keys(): # self._private_css_var.set(opts.get('private_css', 'default')) #else: # self._private_css_var.set('-other-') # self._css_entry.insert(0, opts.get('private_css', 'default')) except Exception as e: log.error('Error opening %s: %s' % (prjfile, e)) self._root.bell()
def open(self, prjfile): from epydoc.docwriter.html_css import STYLESHEETS self._filename = prjfile try: opts = load(open(prjfile, 'r')) modnames = list(opts.get('modules', [])) modnames.sort() self._module_list.delete(0, 'end') for name in modnames: self.add_module(name) self._module_entry.delete(0, 'end') self._name_entry.delete(0, 'end') if opts.get('prj_name'): self._name_entry.insert(0, opts['prj_name']) self._url_entry.delete(0, 'end') if opts.get('prj_url'): self._url_entry.insert(0, opts['prj_url']) self._docformat_var.set(opts.get('docformat', 'epytext')) self._inheritance_var.set(opts.get('inheritance', 'grouped')) self._introspect_or_parse_var.set( opts.get('introspect_or_parse', 'both')) self._help_entry.delete(0, 'end') if opts.get('help') is None: self._help_var.set('default') else: self._help_var.set('-other-') self._help_entry.insert(0, opts.get('help')) self._out_entry.delete(0, 'end') self._out_entry.insert(0, opts.get('outdir', 'html')) self._frames_var.set(opts.get('frames', 1)) self._private_var.set(opts.get('private', 1)) self._imports_var.set(opts.get('show_imports', 0)) self._css_entry.delete(0, 'end') if opts.get('css', 'default') in STYLESHEETS.keys(): self._css_var.set(opts.get('css', 'default')) else: self._css_var.set('-other-') self._css_entry.insert(0, opts.get('css', 'default')) #if opts.get('private_css', 'default') in STYLESHEETS.keys(): # self._private_css_var.set(opts.get('private_css', 'default')) #else: # self._private_css_var.set('-other-') # self._css_entry.insert(0, opts.get('private_css', 'default')) except Exception, e: log.error('Error opening %s: %s' % (prjfile, e)) self._root.bell()
def _save(self, *e): if self._filename is None: return self._saveas() try: opts = self._getopts() dump(opts, open(self._filename, 'w')) except Exception, e: if self._filename is None: log.error('Error saving: %s' % e) else: log.error('Error saving %s: %s' % (self._filename, e)) self._root.bell()
def _save(self, *e): if self._filename is None: return self._saveas() try: opts = self._getopts() dump(opts, open(self._filename, 'w')) except Exception as e: if self._filename is None: log.error('Error saving: %s' % e) else: log.error('Error saving %s: %s' % (self._filename, e)) self._root.bell()
def _get_docs_from_pyobject(obj, introspect, parse, progress_estimator): progress_estimator.complete += 1 log.progress(progress_estimator.progress(), ` obj `) if not introspect: log.error("Cannot get docs for Python objects without " "introspecting them.") introspect_doc = parse_doc = None introspect_error = parse_error = None try: introspect_doc = introspect_docs(value=obj) except ImportError, e: log.error(e) return (None, None)
def _get_docs_from_pyobject(obj, introspect, parse, progress_estimator): progress_estimator.complete += 1 log.progress(progress_estimator.progress(), `obj`) if not introspect: log.error("Cannot get docs for Python objects without " "introspecting them.") introspect_doc = parse_doc = None introspect_error = parse_error = None try: introspect_doc = introspect_docs(value=obj) except ImportError, e: log.error(e) return (None, None)
def document(options, cancel, done): """ Create the documentation for C{modules}, using the options specified by C{options}. C{document} is designed to be started in its own thread by L{EpydocGUI._go}. @param options: The options to use for generating documentation. This includes keyword options that can be given to L{html.HTMLFormatter}, as well as the option C{outdir}, which controls where the output is written to. @type options: C{dictionary} """ from epydoc.docwriter.html import HTMLWriter from epydoc.docbuilder import build_doc_index import epydoc.docstringparser # Set the default docformat. docformat = options.get('docformat', 'epytext') epydoc.docstringparser.DEFAULT_DOCFORMAT = docformat try: parse = options['introspect_or_parse'] in ('parse', 'both') introspect = options['introspect_or_parse'] in ('introspect', 'both') docindex = build_doc_index(options['modules'], parse, introspect) html_writer = HTMLWriter(docindex, **options) log.start_progress('Writing HTML docs to %r' % options['target']) html_writer.write(options['target']) log.end_progress() # We're done. log.warning('Finished!') done[0] = 'done' except SystemExit: # Cancel. log.error('Cancelled!') done[0] = 'cancel' raise except Exception, e: # We failed. log.error('Internal error: %s' % e) done[0] = 'cancel' raise
def document(options, cancel, done): """ Create the documentation for C{modules}, using the options specified by C{options}. C{document} is designed to be started in its own thread by L{EpydocGUI._go}. @param options: The options to use for generating documentation. This includes keyword options that can be given to L{docwriter.html.HTMLWriter}, as well as the option C{target}, which controls where the output is written to. @type options: C{dictionary} """ from epydoc.docwriter.html import HTMLWriter from epydoc.docbuilder import build_doc_index import epydoc.docstringparser # Set the default docformat. docformat = options.get('docformat', 'epytext') epydoc.docstringparser.DEFAULT_DOCFORMAT = docformat try: parse = options['introspect_or_parse'] in ('parse', 'both') introspect = options['introspect_or_parse'] in ('introspect', 'both') docindex = build_doc_index(options['modules'], parse, introspect) html_writer = HTMLWriter(docindex, **options) log.start_progress('Writing HTML docs to %r' % options['target']) html_writer.write(options['target']) log.end_progress() # We're done. log.warning('Finished!') done[0] = 'done' except SystemExit: # Cancel. log.error('Cancelled!') done[0] ='cancel' raise except Exception, e: # We failed. log.error('Internal error: %s' % e) done[0] ='cancel' raise
def _get_docs_from_items(items, introspect, parse, add_submodules): # Start the progress bar. log.start_progress('Building documentation') progress_estimator = _ProgressEstimator(items) # Collect (introspectdoc, parsedoc) pairs for each item. doc_pairs = [] for item in items: if isinstance(item, basestring): if is_module_file(item): doc_pairs.append( _get_docs_from_module_file(item, introspect, parse, progress_estimator)) elif is_package_dir(item): pkgfile = os.path.join(item, '__init__') doc_pairs.append( _get_docs_from_module_file(pkgfile, introspect, parse, progress_estimator)) elif os.path.isfile(item): doc_pairs.append( _get_docs_from_pyscript(item, introspect, parse, progress_estimator)) elif hasattr(__builtin__, item): val = getattr(__builtin__, item) doc_pairs.append( _get_docs_from_pyobject(val, introspect, parse, progress_estimator)) elif is_pyname(item): doc_pairs.append( _get_docs_from_pyname(item, introspect, parse, progress_estimator)) elif os.path.isdir(item): log.error("Directory %r is not a package" % item) elif os.path.isfile(item): log.error("File %s is not a Python module" % item) else: log.error("Could not find a file or object named %s" % item) else: doc_pairs.append( _get_docs_from_pyobject(item, introspect, parse, progress_estimator)) # This will only have an effect if doc_pairs[-1] contains a # package's docs. The 'not is_module_file(item)' prevents # us from adding subdirectories if they explicitly specify # a package's __init__.py file. if add_submodules and not is_module_file(item): doc_pairs += _get_docs_from_submodules(item, doc_pairs[-1], introspect, parse, progress_estimator) log.end_progress() return doc_pairs
def _report_errors(name, introspect_doc, parse_doc, introspect_error, parse_error): hdr = 'In %s:\n' % name if introspect_doc == parse_doc == None: log.start_block('%sNo documentation available!' % hdr) if introspect_error: log.error('Import failed:\n%s' % introspect_error) if parse_error: log.error('Source code parsing failed:\n%s' % parse_error) log.end_block() elif introspect_error: log.start_block('%sImport failed (but source code parsing ' 'was successful).' % hdr) log.error(introspect_error) log.end_block() elif parse_error: log.start_block('%sSource code parsing failed (but ' 'introspection was successful).' % hdr) log.error(parse_error) log.end_block()
def _get_docs_from_items(items, introspect, parse, add_submodules): # Start the progress bar. log.start_progress('Building documentation') progress_estimator = _ProgressEstimator(items) # Collect (introspectdoc, parsedoc) pairs for each item. doc_pairs = [] for item in items: if isinstance(item, basestring): if is_module_file(item): doc_pairs.append(_get_docs_from_module_file( item, introspect, parse, progress_estimator)) elif is_package_dir(item): pkgfile = os.path.join(item, '__init__') doc_pairs.append(_get_docs_from_module_file( pkgfile, introspect, parse, progress_estimator)) elif os.path.isfile(item): doc_pairs.append(_get_docs_from_pyscript( item, introspect, parse, progress_estimator)) elif hasattr(__builtin__, item): val = getattr(__builtin__, item) doc_pairs.append(_get_docs_from_pyobject( val, introspect, parse, progress_estimator)) elif is_pyname(item): doc_pairs.append(_get_docs_from_pyname( item, introspect, parse, progress_estimator)) elif os.path.isdir(item): log.error("Directory %r is not a package" % item) elif os.path.isfile(item): log.error("File %s is not a Python module" % item) else: log.error("Could not find a file or object named %s" % item) else: doc_pairs.append(_get_docs_from_pyobject( item, introspect, parse, progress_estimator)) # This will only have an effect if doc_pairs[-1] contains a # package's docs. The 'not is_module_file(item)' prevents # us from adding subdirectories if they explicitly specify # a package's __init__.py file. if add_submodules and not is_module_file(item): doc_pairs += _get_docs_from_submodules( item, doc_pairs[-1], introspect, parse, progress_estimator) log.end_progress() return doc_pairs
(parse_docstring, markup, e)) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) _markup_language_registry[markup] = parse_docstring # Keep track of which markup languages have been used so far. MARKUP_LANGUAGES_USED.add(markup) # Parse the docstring. try: parsed_docstring = parse_docstring(docstring, errors, **options) except KeyboardInterrupt: raise except Exception, e: if epydoc.DEBUG: raise log.error('Internal error while parsing a docstring: %s; ' 'treating docstring as plaintext' % e) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) # Check for fatal errors. fatal_errors = [e for e in errors if e.is_fatal()] if fatal_errors and raise_on_error: raise fatal_errors[0] if fatal_errors: import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) return parsed_docstring # only issue each warning once: _parse_warnings = {}
def handle_line(self, line): """ Render a single logical line from the module, and write the generated HTML to C{self.out}. @param line: A single logical line, encoded as a list of C{(toktype,tokttext)} pairs corresponding to the tokens in the line. """ # def_name is the name of the function or class defined by # this line; or None if no funciton or class is defined. def_name = None # def_type is the type of the function or class defined by # this line; or None if no funciton or class is defined. def_type = None # does this line start a class/func def? starting_def_block = False in_base_list = False in_param_list = False in_param_default = 0 at_module_top = (self.lineno == 1) ended_def_blocks = 0 # The html output. if self.ADD_LINE_NUMBERS: s = self.lineno_to_html() self.lineno += 1 else: s = '' s += ' <tt class="py-line">' # Loop through each token, and colorize it appropriately. for i, (toktype, toktext) in enumerate(line): if type(s) is not str: if type(s) is str: log.error('While colorizing %s -- got unexpected ' 'unicode string' % self.module_name) s = s.encode('ascii', 'xmlcharrefreplace') else: raise ValueError('Unexpected value for s -- %s' % type(s).__name__) # For each token, determine its css class and whether it # should link to a url. css_class = None url = None tooltip = None onclick = uid = targets = None # these 3 are used together. # Is this token the class name in a class definition? If # so, then make it a link back into the API docs. if i >= 2 and line[i - 2][1] == 'class': in_base_list = True css_class = self.CSS_CLASSES['DEFNAME'] def_name = toktext def_type = 'class' if 'func' not in self.context_types: cls_name = self.context_name(def_name) url = self.name2url(cls_name) s = self.mark_def(s, cls_name) starting_def_block = True # Is this token the function name in a function def? If # so, then make it a link back into the API docs. elif i >= 2 and line[i - 2][1] == 'def': in_param_list = True css_class = self.CSS_CLASSES['DEFNAME'] def_name = toktext def_type = 'func' if 'func' not in self.context_types: cls_name = self.context_name() func_name = self.context_name(def_name) url = self.name2url(cls_name, def_name) s = self.mark_def(s, func_name) starting_def_block = True # For each indent, update the indents list (which we use # to keep track of indentation strings) and the context # list. If this indent is the start of a class or # function def block, then self.def_name will be its name; # otherwise, it will be None. elif toktype == token.INDENT: self.indents.append(toktext) self.context.append(self.def_name) self.context_types.append(self.def_type) # When we dedent, pop the last elements off the indents # list and the context list. If the last context element # is a name, then we're ending a class or function def # block; so write an end-div tag. elif toktype == token.DEDENT: self.indents.pop() self.context_types.pop() if self.context.pop(): ended_def_blocks += 1 # If this token contains whitespace, then don't bother to # give it a css tag. elif toktype in (None, tokenize.NL, token.NEWLINE, token.ENDMARKER): css_class = None # Check if the token is a keyword. elif toktype == token.NAME and keyword.iskeyword(toktext): css_class = self.CSS_CLASSES['KEYWORD'] elif in_base_list and toktype == token.NAME: css_class = self.CSS_CLASSES['BASECLASS'] elif (in_param_list and toktype == token.NAME and not in_param_default): css_class = self.CSS_CLASSES['PARAM'] # Class/function docstring. elif (self.def_name and line[i - 1][0] == token.INDENT and self.is_docstring(line, i)): css_class = self.CSS_CLASSES['DOCSTRING'] # Module docstring. elif at_module_top and self.is_docstring(line, i): css_class = self.CSS_CLASSES['DOCSTRING'] # check for decorators?? elif (toktype == token.NAME and ( (i > 0 and line[i - 1][1] == '@') or (i > 1 and line[i - 1][0] == None and line[i - 2][1] == '@'))): css_class = self.CSS_CLASSES['DECORATOR'] self.has_decorators = True # If it's a name, try to link it. elif toktype == token.NAME: css_class = self.CSS_CLASSES['NAME'] # If we have a variable named `toktext` in the current # context, then link to that. Note that if we're inside # a function, then that function is our context, not # the namespace that contains it. [xx] this isn't always # the right thing to do. if (self.GUESS_LINK_TARGETS and self.docindex is not None and self.url_func is not None): context = [n for n in self.context if n is not None] container = self.docindex.get_vardoc( DottedName(self.module_name, *context)) if isinstance(container, NamespaceDoc): doc = container.variables.get(toktext) if doc is not None: url = self.url_func(doc) tooltip = str(doc.canonical_name) # Otherwise, check the name_to_docs index to see what # else this name might refer to. if (url is None and self.name_to_docs is not None and self.url_func is not None): docs = self.name_to_docs.get(toktext) if docs: tooltip = '\n'.join( [str(d.canonical_name) for d in docs]) if len(docs) == 1 and self.GUESS_LINK_TARGETS: url = self.url_func(docs[0]) else: uid, onclick, targets = self.doclink(toktext, docs) # For all other tokens, look up the CSS class to use # based on the token's type. else: if toktype == token.OP and toktext in self.CSS_CLASSES: css_class = self.CSS_CLASSES[toktext] elif token.tok_name[toktype] in self.CSS_CLASSES: css_class = self.CSS_CLASSES[token.tok_name[toktype]] else: css_class = None # update our status.. if toktext == ':': in_base_list = False in_param_list = False if toktext == '=' and in_param_list: in_param_default = True if in_param_default: if toktext in ('(', '[', '{'): in_param_default += 1 if toktext in (')', ']', '}'): in_param_default -= 1 if toktext == ',' and in_param_default == 1: in_param_default = 0 # Write this token, with appropriate colorization. if tooltip and self.ADD_TOOLTIPS: tooltip_html = ' title="%s"' % tooltip else: tooltip_html = '' if css_class: css_class_html = ' class="%s"' % css_class else: css_class_html = '' if onclick: if targets: targets_html = ' targets="%s"' % targets else: targets_html = '' s += ('<tt id="%s"%s%s><a%s%s href="#" onclick="%s">' % (uid, css_class_html, targets_html, tooltip_html, css_class_html, onclick)) elif url: if isinstance(url, str): url = url.encode('ascii', 'xmlcharrefreplace') s += ('<a%s%s href="%s">' % (tooltip_html, css_class_html, url)) elif css_class_html or tooltip_html: s += '<tt%s%s>' % (tooltip_html, css_class_html) if i == len(line) - 1: s += ' </tt>' # Closes <tt class="py-line"> s += cgi.escape(toktext) else: try: s += self.add_line_numbers(cgi.escape(toktext), css_class) except Exception as e: print((toktext, css_class, toktext.encode('ascii'))) raise if onclick: s += "</a></tt>" elif url: s += '</a>' elif css_class_html or tooltip_html: s += '</tt>' if self.ADD_DEF_BLOCKS: for i in range(ended_def_blocks): self.out(self.END_DEF_BLOCK) # Strip any empty <tt>s. s = re.sub(r'<tt class="[\w+]"></tt>', '', s) # Write the line. self.out(s) if def_name and starting_def_block: self.out('</div>') # Add div's if we're starting a def block. if (self.ADD_DEF_BLOCKS and def_name and starting_def_block and (line[-2][1] == ':')): indentation = (''.join(self.indents) + ' ').replace(' ', '+') linenum_padding = '+' * self.linenum_size name = self.context_name(def_name) self.out(self.START_DEF_BLOCK % (name, linenum_padding, indentation, name)) self.def_name = def_name self.def_type = def_type
except ImportError, e: _parse_warn('Error importing %s for markup language %s: %s' % (parse_docstring, markup, e)) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) _markup_language_registry[markup] = parse_docstring # Keep track of which markup languages have been used so far. MARKUP_LANGUAGES_USED.add(markup) # Parse the docstring. try: parsed_docstring = parse_docstring(docstring, errors, **options) except KeyboardInterrupt: raise except Exception, e: if epydoc.DEBUG: raise log.error('Internal error while parsing a docstring: %s; ' 'treating docstring as plaintext' % e) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) # Check for fatal errors. fatal_errors = [e for e in errors if e.is_fatal()] if fatal_errors and raise_on_error: raise fatal_errors[0] if fatal_errors: import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) return parsed_docstring # only issue each warning once: _parse_warnings = {} def _parse_warn(estr):
def handle_line(self, line): """ Render a single logical line from the module, and write the generated HTML to C{self.out}. @param line: A single logical line, encoded as a list of C{(toktype,tokttext)} pairs corresponding to the tokens in the line. """ # def_name is the name of the function or class defined by # this line; or None if no funciton or class is defined. def_name = None # def_type is the type of the function or class defined by # this line; or None if no funciton or class is defined. def_type = None # does this line start a class/func def? starting_def_block = False in_base_list = False in_param_list = False in_param_default = 0 at_module_top = (self.lineno == 1) ended_def_blocks = 0 # The html output. if self.ADD_LINE_NUMBERS: s = self.lineno_to_html() self.lineno += 1 else: s = '' s += ' <tt class="py-line">' # Loop through each token, and colorize it appropriately. for i, (toktype, toktext) in enumerate(line): if type(s) is not str: if type(s) is str: log.error('While colorizing %s -- got unexpected ' 'unicode string' % self.module_name) s = s.encode('ascii', 'xmlcharrefreplace') else: raise ValueError('Unexpected value for s -- %s' % type(s).__name__) # For each token, determine its css class and whether it # should link to a url. css_class = None url = None tooltip = None onclick = uid = targets = None # these 3 are used together. # Is this token the class name in a class definition? If # so, then make it a link back into the API docs. if i>=2 and line[i-2][1] == 'class': in_base_list = True css_class = self.CSS_CLASSES['DEFNAME'] def_name = toktext def_type = 'class' if 'func' not in self.context_types: cls_name = self.context_name(def_name) url = self.name2url(cls_name) s = self.mark_def(s, cls_name) starting_def_block = True # Is this token the function name in a function def? If # so, then make it a link back into the API docs. elif i>=2 and line[i-2][1] == 'def': in_param_list = True css_class = self.CSS_CLASSES['DEFNAME'] def_name = toktext def_type = 'func' if 'func' not in self.context_types: cls_name = self.context_name() func_name = self.context_name(def_name) url = self.name2url(cls_name, def_name) s = self.mark_def(s, func_name) starting_def_block = True # For each indent, update the indents list (which we use # to keep track of indentation strings) and the context # list. If this indent is the start of a class or # function def block, then self.def_name will be its name; # otherwise, it will be None. elif toktype == token.INDENT: self.indents.append(toktext) self.context.append(self.def_name) self.context_types.append(self.def_type) # When we dedent, pop the last elements off the indents # list and the context list. If the last context element # is a name, then we're ending a class or function def # block; so write an end-div tag. elif toktype == token.DEDENT: self.indents.pop() self.context_types.pop() if self.context.pop(): ended_def_blocks += 1 # If this token contains whitespace, then don't bother to # give it a css tag. elif toktype in (None, tokenize.NL, token.NEWLINE, token.ENDMARKER): css_class = None # Check if the token is a keyword. elif toktype == token.NAME and keyword.iskeyword(toktext): css_class = self.CSS_CLASSES['KEYWORD'] elif in_base_list and toktype == token.NAME: css_class = self.CSS_CLASSES['BASECLASS'] elif (in_param_list and toktype == token.NAME and not in_param_default): css_class = self.CSS_CLASSES['PARAM'] # Class/function docstring. elif (self.def_name and line[i-1][0] == token.INDENT and self.is_docstring(line, i)): css_class = self.CSS_CLASSES['DOCSTRING'] # Module docstring. elif at_module_top and self.is_docstring(line, i): css_class = self.CSS_CLASSES['DOCSTRING'] # check for decorators?? elif (toktype == token.NAME and ((i>0 and line[i-1][1]=='@') or (i>1 and line[i-1][0]==None and line[i-2][1] == '@'))): css_class = self.CSS_CLASSES['DECORATOR'] self.has_decorators = True # If it's a name, try to link it. elif toktype == token.NAME: css_class = self.CSS_CLASSES['NAME'] # If we have a variable named `toktext` in the current # context, then link to that. Note that if we're inside # a function, then that function is our context, not # the namespace that contains it. [xx] this isn't always # the right thing to do. if (self.GUESS_LINK_TARGETS and self.docindex is not None and self.url_func is not None): context = [n for n in self.context if n is not None] container = self.docindex.get_vardoc( DottedName(self.module_name, *context)) if isinstance(container, NamespaceDoc): doc = container.variables.get(toktext) if doc is not None: url = self.url_func(doc) tooltip = str(doc.canonical_name) # Otherwise, check the name_to_docs index to see what # else this name might refer to. if (url is None and self.name_to_docs is not None and self.url_func is not None): docs = self.name_to_docs.get(toktext) if docs: tooltip='\n'.join([str(d.canonical_name) for d in docs]) if len(docs) == 1 and self.GUESS_LINK_TARGETS: url = self.url_func(docs[0]) else: uid, onclick, targets = self.doclink(toktext, docs) # For all other tokens, look up the CSS class to use # based on the token's type. else: if toktype == token.OP and toktext in self.CSS_CLASSES: css_class = self.CSS_CLASSES[toktext] elif token.tok_name[toktype] in self.CSS_CLASSES: css_class = self.CSS_CLASSES[token.tok_name[toktype]] else: css_class = None # update our status.. if toktext == ':': in_base_list = False in_param_list = False if toktext == '=' and in_param_list: in_param_default = True if in_param_default: if toktext in ('(','[','{'): in_param_default += 1 if toktext in (')',']','}'): in_param_default -= 1 if toktext == ',' and in_param_default == 1: in_param_default = 0 # Write this token, with appropriate colorization. if tooltip and self.ADD_TOOLTIPS: tooltip_html = ' title="%s"' % tooltip else: tooltip_html = '' if css_class: css_class_html = ' class="%s"' % css_class else: css_class_html = '' if onclick: if targets: targets_html = ' targets="%s"' % targets else: targets_html = '' s += ('<tt id="%s"%s%s><a%s%s href="#" onclick="%s">' % (uid, css_class_html, targets_html, tooltip_html, css_class_html, onclick)) elif url: if isinstance(url, str): url = url.encode('ascii', 'xmlcharrefreplace') s += ('<a%s%s href="%s">' % (tooltip_html, css_class_html, url)) elif css_class_html or tooltip_html: s += '<tt%s%s>' % (tooltip_html, css_class_html) if i == len(line)-1: s += ' </tt>' # Closes <tt class="py-line"> s += cgi.escape(toktext) else: try: s += self.add_line_numbers(cgi.escape(toktext), css_class) except Exception as e: print((toktext, css_class, toktext.encode('ascii'))) raise if onclick: s += "</a></tt>" elif url: s += '</a>' elif css_class_html or tooltip_html: s += '</tt>' if self.ADD_DEF_BLOCKS: for i in range(ended_def_blocks): self.out(self.END_DEF_BLOCK) # Strip any empty <tt>s. s = re.sub(r'<tt class="[\w+]"></tt>', '', s) # Write the line. self.out(s) if def_name and starting_def_block: self.out('</div>') # Add div's if we're starting a def block. if (self.ADD_DEF_BLOCKS and def_name and starting_def_block and (line[-2][1] == ':')): indentation = (''.join(self.indents)+' ').replace(' ', '+') linenum_padding = '+'*self.linenum_size name=self.context_name(def_name) self.out(self.START_DEF_BLOCK % (name, linenum_padding, indentation, name)) self.def_name = def_name self.def_type = def_type
def parse(docstring, markup='plaintext', errors=None, **options): """ Parse the given docstring, and use it to construct a C{ParsedDocstring}. If any fatal C{ParseError}s are encountered while parsing the docstring, then the docstring will be rendered as plaintext, instead. @type docstring: C{string} @param docstring: The docstring to encode. @type markup: C{string} @param markup: The name of the markup language that is used by the docstring. If the markup language is not supported, then the docstring will be treated as plaintext. The markup name is case-insensitive. @param errors: A list where any errors generated during parsing will be stored. If no list is specified, then fatal errors will generate exceptions, and non-fatal errors will be ignored. @type errors: C{list} of L{ParseError} @rtype: L{ParsedDocstring} @return: A L{ParsedDocstring} that encodes the contents of C{docstring}. @raise ParseError: If C{errors} is C{None} and an error is encountered while parsing. """ # Initialize errors list. raise_on_error = (errors is None) if errors == None: errors = [] # Normalize the markup language name. markup = markup.lower() # Is the markup language valid? if not re.match(r'\w+', markup): _parse_warn('Bad markup language name %r. Treating ' 'docstrings as plaintext.' % markup) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) # Is the markup language supported? if markup not in _markup_language_registry: _parse_warn('Unsupported markup language %r. Treating ' 'docstrings as plaintext.' % markup) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) # Get the parse function. parse_docstring = _markup_language_registry[markup] # If it's a string, then it names a function to import. if isinstance(parse_docstring, str): try: exec('from %s import parse_docstring' % parse_docstring) except ImportError as e: _parse_warn('Error importing %s for markup language %s: %s' % (parse_docstring, markup, e)) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) _markup_language_registry[markup] = parse_docstring # Keep track of which markup languages have been used so far. MARKUP_LANGUAGES_USED.add(markup) # Parse the docstring. try: parsed_docstring = parse_docstring(docstring, errors, **options) except KeyboardInterrupt: raise except Exception as e: if epydoc.DEBUG: raise log.error('Internal error while parsing a docstring: %s; ' 'treating docstring as plaintext' % e) import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) # Check for fatal errors. fatal_errors = [e for e in errors if e.is_fatal()] if fatal_errors and raise_on_error: raise fatal_errors[0] if fatal_errors: import epydoc.markup.plaintext as plaintext return plaintext.parse_docstring(docstring, errors, **options) return parsed_docstring
def _colorize_re(tree, noparen=0): """ Recursively descend the given regexp parse tree to produce the HTML code for a colorized version of the regexp. @param tree: The regexp parse tree for the regexp that should be colorized. @type tree: L{sre_parse.SubPattern} @param noparen: If true, then don't include parenthases around the expression in C{tree}, even if it contains multiple elements. @type noparen: C{boolean} @return: The HTML code for a colorized version of C{tree} @rtype: C{string} """ result = [] out = result.append if len(tree) > 1 and not noparen: out('<span class="%s">(</span>' % PAREN_TAG) for elt in tree: op = elt[0] args = elt[1] if op == sre_constants.LITERAL: c = unichr(args) if c == '\t': out(r'<span class="%s">\t</span>' % ESCAPE_TAG) elif c == '\n': out(r'<span class="%s">\n</span>' % ESCAPE_TAG) elif c == '\r': out(r'<span class="%s">\r</span>' % ESCAPE_TAG) elif c == '\f': out(r'<span class="%s">\f</span>' % ESCAPE_TAG) elif c == '\v': out(r'<span class="%s">\v</span>' % ESCAPE_TAG) elif ord(c)<32 or ord(c)>=127: if c < 256: template = r'<span class="%s">\x%02x</span>' else: template = r'<span class="%s">\u%04x</span>' out(template % (ESCAPE_TAG,ord(c))) elif c in '.^$\\*+?{}[]|()': out(r'<span class="%s">\%c</span>' % (ESCAPE_TAG, c)) else: out(plaintext_to_html(unichr(args))) continue elif op == sre_constants.ANY: out('<span class="%s">.</span>' % ANY_TAG) elif op == sre_constants.BRANCH: if args[0] is not None: raise ValueError('Branch expected None arg but got %s' % args[0]) VBAR = '<span class="%s">|</span>' % BRANCH_TAG out(VBAR.join([_colorize_re(item,1) for item in args[1]])) elif op == sre_constants.IN: if (len(args) == 1 and args[0][0] == sre_constants.CATEGORY): out(_colorize_re(args)) else: out('<span class="%s">[</span>' % CHOICE_TAG) out(_colorize_re(args, 1)) out('<span class="%s">]</span>' % CHOICE_TAG) elif op == sre_constants.CATEGORY: out('<span class="%s">' % CATEGORY_TAG) if args == sre_constants.CATEGORY_DIGIT: out(r'\d') elif args == sre_constants.CATEGORY_NOT_DIGIT: out(r'\D') elif args == sre_constants.CATEGORY_SPACE: out(r'\s') elif args == sre_constants.CATEGORY_NOT_SPACE: out(r'\S') elif args == sre_constants.CATEGORY_WORD: out(r'\w') elif args == sre_constants.CATEGORY_NOT_WORD: out(r'\W') else: raise ValueError('Unknown category %s' % args) out('</span>') elif op == sre_constants.AT: out('<span class="%s">' % AT_TAG) if args == sre_constants.AT_BEGINNING_STRING: out(r'\A') elif args == sre_constants.AT_BEGINNING: out(r'^') elif args == sre_constants.AT_END: out(r'$') elif args == sre_constants.AT_BOUNDARY: out(r'\b') elif args == sre_constants.AT_NON_BOUNDARY: out(r'\B') elif args == sre_constants.AT_END_STRING: out(r'\Z') else: raise ValueError('Unknown position %s' % args) out('</span>') elif op == sre_constants.MAX_REPEAT: min = args[0] max = args[1] if max == sre_constants.MAXREPEAT: if min == 0: out(_colorize_re(args[2])) out('<span class="%s">*</span>' % STAR_TAG) elif min == 1: out(_colorize_re(args[2])) out('<span class="%s">+</span>' % PLUS_TAG) else: out(_colorize_re(args[2])) out('<span class="%s">{%d,}</span>' % (RNG_TAG, min)) elif min == 0: if max == 1: out(_colorize_re(args[2])) out('<span class="%s">?</span>' % QMRK_TAG) else: out(_colorize_re(args[2])) out('<span class="%s">{,%d}</span>' % (RNG_TAG, max)) elif min == max: out(_colorize_re(args[2])) out('<span class="%s">{%d}</span>' % (RNG_TAG, max)) else: out(_colorize_re(args[2])) out('<span class="%s">{%d,%d}</span>' % (RNG_TAG, min, max)) elif op == sre_constants.MIN_REPEAT: min = args[0] max = args[1] if max == sre_constants.MAXREPEAT: if min == 0: out(_colorize_re(args[2])) out('<span class="%s">*?</span>' % STAR_TAG) elif min == 1: out(_colorize_re(args[2])) out('<span class="%s">+?</span>' % PLUS_TAG) else: out(_colorize_re(args[2])) out('<span class="%s">{%d,}?</span>' % (RNG_TAG, min)) elif min == 0: if max == 1: out(_colorize_re(args[2])) out('<span class="%s">??</span>' % QMRK_TAG) else: out(_colorize_re(args[2])) out('<span class="%s">{,%d}?</span>' % (RNG_TAG, max)) elif min == max: out(_colorize_re(args[2])) out('<span class="%s">{%d}?</span>' % (RNG_TAG, max)) else: out(_colorize_re(args[2])) out('<span class="%s">{%d,%d}?</span>'%(RNG_TAG, min, max)) elif op == sre_constants.SUBPATTERN: if args[0] is None: out('<span class="%s">(?:</span>' % PAREN_TAG) elif isinstance(args[0], (int, long)): # This is cheating: out('<span class="%s">(</span>' % PAREN_TAG) else: out('<span class="%s">(?P<</span>' % PAREN_TAG) out('<span class="%s">%s</span>' % (REF_TAG, plaintext_to_html(args[0]))) out('<span class="%s">></span>' % PAREN_TAG) out(_colorize_re(args[1], 1)) out('<span class="%s">)</span>' % PAREN_TAG) elif op == sre_constants.GROUPREF: out('<span class="%s">\\%d</span>' % (REF_TAG, args)) elif op == sre_constants.RANGE: start = _colorize_re( ((sre_constants.LITERAL, args[0]),) ) end = _colorize_re( ((sre_constants.LITERAL, args[1]),) ) out('%s<span class="%s">-</span>%s' % (start, CHOICE_TAG, end)) elif op == sre_constants.NEGATE: out('<span class="%s">^</span>' % CHOICE_TAG) elif op == sre_constants.ASSERT: if args[0]: out('<span class="%s">(?=</span>' % ASSERT_TAG) else: out('<span class="%s">(?<=</span>' % ASSERT_TAG) out(''.join(_colorize_re(args[1], 1))) out('<span class="%s">)</span>' % ASSERT_TAG) elif op == sre_constants.ASSERT_NOT: if args[0]: out('<span class="%s">(?!</span>' % ASSERT_TAG) else: out('<span class="%s">(?<!</span>' % ASSERT_TAG) out(''.join(_colorize_re(args[1], 1))) out('<span class="%s">)</span>' % ASSERT_TAG) elif op == sre_constants.NOT_LITERAL: lit = _colorize_re( ((sre_constants.LITERAL, args),) ) out('<span class="%s">[^</span>%s<span class="%s">]</span>' % (CHOICE_TAG, lit, CHOICE_TAG)) else: log.error("Error colorizing regexp: unknown elt %r" % elt) if len(tree) > 1 and not noparen: out('<span class="%s">)</span>' % PAREN_TAG) return u''.join(result)
def write_latex(docindex, options, format): from epydoc.docwriter.latex import LatexWriter latex_writer = LatexWriter(docindex, **options.__dict__) log.start_progress('Writing LaTeX docs') latex_writer.write(options.target) log.end_progress() # If we're just generating the latex, and not any output format, # then we're done. if format == 'latex': return if format == 'dvi': steps = 4 elif format == 'ps': steps = 5 elif format == 'pdf': steps = 6 log.start_progress('Processing LaTeX docs') oldpath = os.path.abspath(os.curdir) running = None # keep track of what we're doing. try: try: os.chdir(options.target) # Clear any old files out of the way. for ext in 'tex aux log out idx ilg toc ind'.split(): if os.path.exists('apidoc.%s' % ext): os.remove('apidoc.%s' % ext) # The first pass generates index files. running = 'latex' log.progress(0./steps, 'LaTeX: First pass') run_subprocess('latex api.tex') # Build the index. running = 'makeindex' log.progress(1./steps, 'LaTeX: Build index') run_subprocess('makeindex api.idx') # The second pass generates our output. running = 'latex' log.progress(2./steps, 'LaTeX: Second pass') out, err = run_subprocess('latex api.tex') # The third pass is only necessary if the second pass # changed what page some things are on. running = 'latex' if _RERUN_LATEX_RE.match(out): log.progress(3./steps, 'LaTeX: Third pass') out, err = run_subprocess('latex api.tex') # A fourth path should (almost?) never be necessary. running = 'latex' if _RERUN_LATEX_RE.match(out): log.progress(3./steps, 'LaTeX: Fourth pass') run_subprocess('latex api.tex') # If requested, convert to postscript. if format in ('ps', 'pdf'): running = 'dvips' log.progress(4./steps, 'dvips') run_subprocess('dvips api.dvi -o api.ps -G0 -Ppdf') # If requested, convert to pdf. if format in ('pdf'): running = 'ps2pdf' log.progress(5./steps, 'ps2pdf') run_subprocess( 'ps2pdf -sPAPERSIZE=letter -dMaxSubsetPct=100 ' '-dSubsetFonts=true -dCompatibilityLevel=1.2 ' '-dEmbedAllFonts=true api.ps api.pdf') except RunSubprocessError, e: if running == 'latex': e.out = re.sub(r'(?sm)\A.*?!( LaTeX Error:)?', r'', e.out) e.out = re.sub(r'(?sm)\s*Type X to quit.*', '', e.out) e.out = re.sub(r'(?sm)^! Emergency stop.*', '', e.out) log.error("%s failed: %s" % (running, (e.out+e.err).lstrip())) except OSError, e: log.error("%s failed: %s" % (running, e))
def build_doc_index(items, introspect=True, parse=True, add_submodules=True): """ Build API documentation for the given list of items, and return it in the form of a L{DocIndex}. @rtype: L{DocIndex} @param items: The items to document, specified using any of the following: - A string, naming a python package directory (e.g., C{'epydoc/markup'}) - A string, naming a python file (e.g., C{'epydoc/docparser.py'}) - A string, naming a python object (e.g., C{'epydoc.docparser.DocParser'}) - Any (non-string) python object (e.g., C{list.append}) @param introspect: If true, then use introspection to examine the specified items. Otherwise, just use parsing. @param parse: If true, then use parsing to examine the specified items. Otherwise, just use introspection. """ # Get the basic docs for each item. doc_pairs = _get_docs_from_items(items, introspect, parse, add_submodules) # Merge the introspection & parse docs. if parse and introspect: log.start_progress('Merging parsed & introspected information') docs = [] for i, (introspect_doc, parse_doc) in enumerate(doc_pairs): if introspect_doc is not None and parse_doc is not None: if introspect_doc.canonical_name not in (None, UNKNOWN): name = introspect_doc.canonical_name else: name = parse_doc.canonical_name log.progress(float(i)/len(doc_pairs), name) docs.append(merge_docs(introspect_doc, parse_doc)) elif introspect_doc is not None: docs.append(introspect_doc) elif parse_doc is not None: docs.append(parse_doc) log.end_progress() elif introspect: docs = [doc_pair[0] for doc_pair in doc_pairs if doc_pair[0]] else: docs = [doc_pair[1] for doc_pair in doc_pairs if doc_pair[1]] if len(docs) == 0: log.error('Nothing left to document!') return None # Collect the docs into a single index. docindex = DocIndex(docs) # Replace any proxy valuedocs that we got from importing with # their targets. if parse: log.start_progress('Linking imported variables') valdocs = docindex.reachable_valdocs(sort_by_name=True, imports=False, submodules=False, packages=False, subclasses=False) for i, val_doc in enumerate(valdocs): _report_valdoc_progress(i, val_doc, valdocs) link_imports(val_doc, docindex) log.end_progress() # Assign canonical names. log.start_progress('Indexing documentation') for i, val_doc in enumerate(docindex.root): log.progress(float(i)/len(docindex.root), val_doc.canonical_name) assign_canonical_names(val_doc, val_doc.canonical_name, docindex) log.end_progress() # Parse the docstrings for each object. log.start_progress('Parsing docstrings') valdocs = docindex.reachable_valdocs(sort_by_name=True, imports=False, submodules=False, packages=False, subclasses=False) for i, val_doc in enumerate(valdocs): _report_valdoc_progress(i, val_doc, valdocs) # the value's docstring parse_docstring(val_doc, docindex) # the value's variables' docstrings if (isinstance(val_doc, NamespaceDoc) and val_doc.variables not in (None, UNKNOWN)): for var_doc in val_doc.variables.values(): parse_docstring(var_doc, docindex) log.end_progress() # Take care of inheritance. log.start_progress('Inheriting documentation') for i, val_doc in enumerate(valdocs): if isinstance(val_doc, ClassDoc): percent = float(i)/len(valdocs) log.progress(percent, val_doc.canonical_name) inherit_docs(val_doc) log.end_progress() # Initialize the groups & sortedvars attributes. log.start_progress('Sorting & Grouping') for i, val_doc in enumerate(valdocs): if isinstance(val_doc, NamespaceDoc): percent = float(i)/len(valdocs) log.progress(percent, val_doc.canonical_name) val_doc.init_sorted_variables() val_doc.init_variable_groups() if isinstance(val_doc, ModuleDoc): val_doc.init_submodule_groups() log.end_progress() return docindex
log.warning('Finished!') done[0] = 'done' except SystemExit: # Cancel. log.error('Cancelled!') done[0] ='cancel' raise except Exception, e: # We failed. log.error('Internal error: %s' % e) done[0] ='cancel' raise except: # We failed. log.error('Internal error!') done[0] ='cancel' raise ##///////////////////////////////////////////////////////////////////////// ## GUI ##///////////////////////////////////////////////////////////////////////// class EpydocGUI: """ A graphical user interace to epydoc. """ def __init__(self): self._afterid = 0 self._progress = [None] self._cancel = [0]
def main(options, names): if options.action == 'text': if options.parse and options.introspect: options.parse = False # Set up the logger if options.action == 'text': logger = None # no logger for text output. elif options.verbosity > 1: logger = ConsoleLogger(options.verbosity) log.register_logger(logger) else: # Each number is a rough approximation of how long we spend on # that task, used to divide up the unified progress bar. stages = [40, # Building documentation 7, # Merging parsed & introspected information 1, # Linking imported variables 3, # Indexing documentation 30, # Parsing Docstrings 1, # Inheriting documentation 2] # Sorting & Grouping if options.action == 'html': stages += [100] elif options.action == 'text': stages += [30] elif options.action == 'latex': stages += [60] elif options.action == 'dvi': stages += [60,30] elif options.action == 'ps': stages += [60,40] elif options.action == 'pdf': stages += [60,50] elif options.action == 'check': stages += [10] else: raise ValueError, '%r not supported' % options.action if options.parse and not options.introspect: del stages[1] # no merging if options.introspect and not options.parse: del stages[1:3] # no merging or linking logger = UnifiedProgressConsoleLogger(options.verbosity, stages) log.register_logger(logger) # check the output directory. if options.action != 'text': if os.path.exists(options.target): if not os.path.isdir(options.target): return log.error("%s is not a directory" % options.target) # Set the default docformat from epydoc import docstringparser docstringparser.DEFAULT_DOCFORMAT = options.docformat # Set the dot path if options.dotpath: from epydoc import dotgraph dotgraph.DOT_PATH = options.dotpath # Build docs for the named values. from epydoc.docbuilder import build_doc_index docindex = build_doc_index(names, options.introspect, options.parse, add_submodules=(options.action!='text')) if docindex is None: return # docbuilder already logged an error. # Load profile information, if it was given. if options.pstat_files: try: profile_stats = pstats.Stats(options.pstat_files[0]) for filename in options.pstat_files[1:]: profile_stats.add(filename) except KeyboardInterrupt: raise except Exception, e: log.error("Error reading pstat file: %s" % e) profile_stats = None if profile_stats is not None: docindex.read_profiling_info(profile_stats)
def handle_line(self, line): """ Render a single logical line from the module, and write the generated HTML to C{self.out}. @param line: A single logical line, encoded as a list of C{(toktype,tokttext)} pairs corresponding to the tokens in the line. """ # def_name is the name of the function or class defined by # this line; or None if no funciton or class is defined. def_name = None # def_type is the type of the function or class defined by # this line; or None if no funciton or class is defined. def_type = None # does this line start a class/func def? starting_def_block = False in_base_list = False in_param_list = False in_param_default = 0 at_module_top = self.lineno == 1 ended_def_blocks = 0 # The html output. if self.ADD_LINE_NUMBERS: s = self.lineno_to_html() self.lineno += 1 else: s = "" s += ' <tt class="py-line">' # Loop through each token, and colorize it appropriately. for i, (toktype, toktext) in enumerate(line): if type(s) is not str: if type(s) is unicode: log.error("While colorizing %s -- got unexpected " "unicode string" % self.module_name) s = s.encode("ascii", "xmlcharrefreplace") else: raise ValueError("Unexpected value for s -- %s" % type(s).__name__) # For each token, determine its css class and whether it # should link to a url. css_class = None url = None tooltip = None onclick = uid = targets = None # these 3 are used together. # Is this token the class name in a class definition? If # so, then make it a link back into the API docs. if i >= 2 and line[i - 2][1] == "class": in_base_list = True css_class = self.CSS_CLASSES["DEFNAME"] def_name = toktext def_type = "class" if "func" not in self.context_types: cls_name = self.context_name(def_name) url = self.name2url(cls_name) s = self.mark_def(s, cls_name) starting_def_block = True # Is this token the function name in a function def? If # so, then make it a link back into the API docs. elif i >= 2 and line[i - 2][1] == "def": in_param_list = True css_class = self.CSS_CLASSES["DEFNAME"] def_name = toktext def_type = "func" if "func" not in self.context_types: cls_name = self.context_name() func_name = self.context_name(def_name) url = self.name2url(cls_name, def_name) s = self.mark_def(s, func_name) starting_def_block = True # For each indent, update the indents list (which we use # to keep track of indentation strings) and the context # list. If this indent is the start of a class or # function def block, then self.def_name will be its name; # otherwise, it will be None. elif toktype == token.INDENT: self.indents.append(toktext) self.context.append(self.def_name) self.context_types.append(self.def_type) # When we dedent, pop the last elements off the indents # list and the context list. If the last context element # is a name, then we're ending a class or function def # block; so write an end-div tag. elif toktype == token.DEDENT: self.indents.pop() self.context_types.pop() if self.context.pop(): ended_def_blocks += 1 # If this token contains whitespace, then don't bother to # give it a css tag. elif toktype in (None, tokenize.NL, token.NEWLINE, token.ENDMARKER): css_class = None # Check if the token is a keyword. elif toktype == token.NAME and keyword.iskeyword(toktext): css_class = self.CSS_CLASSES["KEYWORD"] elif in_base_list and toktype == token.NAME: css_class = self.CSS_CLASSES["BASECLASS"] elif in_param_list and toktype == token.NAME and not in_param_default: css_class = self.CSS_CLASSES["PARAM"] # Class/function docstring. elif self.def_name and line[i - 1][0] == token.INDENT and self.is_docstring(line, i): css_class = self.CSS_CLASSES["DOCSTRING"] # Module docstring. elif at_module_top and self.is_docstring(line, i): css_class = self.CSS_CLASSES["DOCSTRING"] # check for decorators?? elif toktype == token.NAME and ( (i > 0 and line[i - 1][1] == "@") or (i > 1 and line[i - 1][0] == None and line[i - 2][1] == "@") ): css_class = self.CSS_CLASSES["DECORATOR"] # If it's a name, try to link it. elif toktype == token.NAME: css_class = self.CSS_CLASSES["NAME"] # If we have a variable named `toktext` in the current # context, then link to that. Note that if we're inside # a function, then that function is our context, not # the namespace that contains it. [xx] this isn't always # the right thing to do. if self.GUESS_LINK_TARGETS and self.docindex is not None and self.url_func is not None: context = [n for n in self.context if n is not None] container = self.docindex.get_vardoc(DottedName(self.module_name, *context)) if isinstance(container, NamespaceDoc): doc = container.variables.get(toktext) if doc is not None: url = self.url_func(doc) tooltip = str(doc.canonical_name) # Otherwise, check the name_to_docs index to see what # else this name might refer to. if url is None and self.name_to_docs is not None and self.url_func is not None: docs = self.name_to_docs.get(toktext) if docs: tooltip = "\n".join([str(d.canonical_name) for d in docs]) if len(docs) == 1 and self.GUESS_LINK_TARGETS: url = self.url_func(docs[0]) else: uid, onclick, targets = self.doclink(toktext, docs) # For all other tokens, look up the CSS class to use # based on the token's type. else: if toktype == token.OP and toktext in self.CSS_CLASSES: css_class = self.CSS_CLASSES[toktext] elif token.tok_name[toktype] in self.CSS_CLASSES: css_class = self.CSS_CLASSES[token.tok_name[toktype]] else: css_class = None # update our status.. if toktext == ":": in_base_list = False in_param_list = False if toktext == "=" and in_param_list: in_param_default = True if in_param_default: if toktext in ("(", "[", "{"): in_param_default += 1 if toktext in (")", "]", "}"): in_param_default -= 1 if toktext == "," and in_param_default == 1: in_param_default = 0 # Write this token, with appropriate colorization. if tooltip and self.ADD_TOOLTIPS: tooltip_html = ' title="%s"' % tooltip else: tooltip_html = "" if css_class: css_class_html = ' class="%s"' % css_class else: css_class_html = "" if onclick: if targets: targets_html = ' targets="%s"' % targets else: targets_html = "" s += '<tt id="%s"%s%s><a%s%s href="#" onclick="%s">' % ( uid, css_class_html, targets_html, tooltip_html, css_class_html, onclick, ) elif url: if isinstance(url, unicode): url = url.encode("ascii", "xmlcharrefreplace") s += '<a%s%s href="%s">' % (tooltip_html, css_class_html, url) elif css_class_html or tooltip_html: s += "<tt%s%s>" % (tooltip_html, css_class_html) if i == len(line) - 1: s += " </tt>" # Closes <tt class="py-line"> s += cgi.escape(toktext) else: try: s += self.add_line_numbers(cgi.escape(toktext), css_class) except Exception, e: print(toktext, css_class, toktext.encode("ascii")) raise if onclick: s += "</a></tt>" elif url: s += "</a>" elif css_class_html or tooltip_html: s += "</tt>"
log.warning('Finished!') done[0] = 'done' except SystemExit: # Cancel. log.error('Cancelled!') done[0] = 'cancel' raise except Exception, e: # We failed. log.error('Internal error: %s' % e) done[0] = 'cancel' raise except: # We failed. log.error('Internal error!') done[0] = 'cancel' raise ##///////////////////////////////////////////////////////////////////////// ## GUI ##///////////////////////////////////////////////////////////////////////// class EpydocGUI: """ A graphical user interace to epydoc. """ def __init__(self): self._afterid = 0
def main(options, names): if options.action == 'text': if options.parse and options.introspect: options.parse = False # Set up the logger if options.action == 'text': logger = None # no logger for text output. elif options.verbosity > 1: logger = ConsoleLogger(options.verbosity) log.register_logger(logger) else: # Each number is a rough approximation of how long we spend on # that task, used to divide up the unified progress bar. stages = [ 40, # Building documentation 7, # Merging parsed & introspected information 1, # Linking imported variables 3, # Indexing documentation 30, # Parsing Docstrings 1, # Inheriting documentation 2 ] # Sorting & Grouping if options.action == 'html': stages += [100] elif options.action == 'text': stages += [30] elif options.action == 'latex': stages += [60] elif options.action == 'dvi': stages += [60, 30] elif options.action == 'ps': stages += [60, 40] elif options.action == 'pdf': stages += [60, 50] elif options.action == 'check': stages += [10] else: raise ValueError, '%r not supported' % options.action if options.parse and not options.introspect: del stages[1] # no merging if options.introspect and not options.parse: del stages[1:3] # no merging or linking logger = UnifiedProgressConsoleLogger(options.verbosity, stages) log.register_logger(logger) # check the output directory. if options.action != 'text': if os.path.exists(options.target): if not os.path.isdir(options.target): return log.error("%s is not a directory" % options.target) # Set the default docformat from epydoc import docstringparser docstringparser.DEFAULT_DOCFORMAT = options.docformat # Set the dot path if options.dotpath: from epydoc import dotgraph dotgraph.DOT_PATH = options.dotpath # Build docs for the named values. from epydoc.docbuilder import build_doc_index docindex = build_doc_index(names, options.introspect, options.parse, add_submodules=(options.action != 'text')) if docindex is None: return # docbuilder already logged an error. # Load profile information, if it was given. if options.pstat_files: try: profile_stats = pstats.Stats(options.pstat_files[0]) for filename in options.pstat_files[1:]: profile_stats.add(filename) except KeyboardInterrupt: raise except Exception, e: log.error("Error reading pstat file: %s" % e) profile_stats = None if profile_stats is not None: docindex.read_profiling_info(profile_stats)
def write_latex(docindex, options, format): from epydoc.docwriter.latex import LatexWriter latex_writer = LatexWriter(docindex, **options.__dict__) log.start_progress('Writing LaTeX docs') latex_writer.write(options.target) log.end_progress() # If we're just generating the latex, and not any output format, # then we're done. if format == 'latex': return if format == 'dvi': steps = 4 elif format == 'ps': steps = 5 elif format == 'pdf': steps = 6 log.start_progress('Processing LaTeX docs') oldpath = os.path.abspath(os.curdir) running = None # keep track of what we're doing. try: try: os.chdir(options.target) # Clear any old files out of the way. for ext in 'tex aux log out idx ilg toc ind'.split(): if os.path.exists('apidoc.%s' % ext): os.remove('apidoc.%s' % ext) # The first pass generates index files. running = 'latex' log.progress(0. / steps, 'LaTeX: First pass') run_subprocess('latex api.tex') # Build the index. running = 'makeindex' log.progress(1. / steps, 'LaTeX: Build index') run_subprocess('makeindex api.idx') # The second pass generates our output. running = 'latex' log.progress(2. / steps, 'LaTeX: Second pass') out, err = run_subprocess('latex api.tex') # The third pass is only necessary if the second pass # changed what page some things are on. running = 'latex' if _RERUN_LATEX_RE.match(out): log.progress(3. / steps, 'LaTeX: Third pass') out, err = run_subprocess('latex api.tex') # A fourth path should (almost?) never be necessary. running = 'latex' if _RERUN_LATEX_RE.match(out): log.progress(3. / steps, 'LaTeX: Fourth pass') run_subprocess('latex api.tex') # If requested, convert to postscript. if format in ('ps', 'pdf'): running = 'dvips' log.progress(4. / steps, 'dvips') run_subprocess('dvips api.dvi -o api.ps -G0 -Ppdf') # If requested, convert to pdf. if format in ('pdf'): running = 'ps2pdf' log.progress(5. / steps, 'ps2pdf') run_subprocess('ps2pdf -sPAPERSIZE=letter -dMaxSubsetPct=100 ' '-dSubsetFonts=true -dCompatibilityLevel=1.2 ' '-dEmbedAllFonts=true api.ps api.pdf') except RunSubprocessError, e: if running == 'latex': e.out = re.sub(r'(?sm)\A.*?!( LaTeX Error:)?', r'', e.out) e.out = re.sub(r'(?sm)\s*Type X to quit.*', '', e.out) e.out = re.sub(r'(?sm)^! Emergency stop.*', '', e.out) log.error("%s failed: %s" % (running, (e.out + e.err).lstrip())) except OSError, e: log.error("%s failed: %s" % (running, e))
def build_doc_index(items, introspect=True, parse=True, add_submodules=True): """ Build API documentation for the given list of items, and return it in the form of a L{DocIndex}. @rtype: L{DocIndex} @param items: The items to document, specified using any of the following: - A string, naming a python package directory (e.g., C{'epydoc/markup'}) - A string, naming a python file (e.g., C{'epydoc/docparser.py'}) - A string, naming a python object (e.g., C{'epydoc.docparser.DocParser'}) - Any (non-string) python object (e.g., C{list.append}) @param introspect: If true, then use introspection to examine the specified items. Otherwise, just use parsing. @param parse: If true, then use parsing to examine the specified items. Otherwise, just use introspection. """ # Get the basic docs for each item. doc_pairs = _get_docs_from_items(items, introspect, parse, add_submodules) # Merge the introspection & parse docs. if parse and introspect: log.start_progress('Merging parsed & introspected information') docs = [] for i, (introspect_doc, parse_doc) in enumerate(doc_pairs): if introspect_doc is not None and parse_doc is not None: if introspect_doc.canonical_name not in (None, UNKNOWN): name = introspect_doc.canonical_name else: name = parse_doc.canonical_name log.progress(float(i) / len(doc_pairs), name) docs.append(merge_docs(introspect_doc, parse_doc)) elif introspect_doc is not None: docs.append(introspect_doc) elif parse_doc is not None: docs.append(parse_doc) log.end_progress() elif introspect: docs = [doc_pair[0] for doc_pair in doc_pairs if doc_pair[0]] else: docs = [doc_pair[1] for doc_pair in doc_pairs if doc_pair[1]] if len(docs) == 0: log.error('Nothing left to document!') return None # Collect the docs into a single index. docindex = DocIndex(docs) # Replace any proxy valuedocs that we got from importing with # their targets. if parse: log.start_progress('Linking imported variables') valdocs = docindex.reachable_valdocs(sort_by_name=True, imports=False, submodules=False, packages=False, subclasses=False) for i, val_doc in enumerate(valdocs): _report_valdoc_progress(i, val_doc, valdocs) link_imports(val_doc, docindex) log.end_progress() # Assign canonical names. log.start_progress('Indexing documentation') for i, val_doc in enumerate(docindex.root): log.progress(float(i) / len(docindex.root), val_doc.canonical_name) assign_canonical_names(val_doc, val_doc.canonical_name, docindex) log.end_progress() # Parse the docstrings for each object. log.start_progress('Parsing docstrings') valdocs = docindex.reachable_valdocs(sort_by_name=True, imports=False, submodules=False, packages=False, subclasses=False) for i, val_doc in enumerate(valdocs): _report_valdoc_progress(i, val_doc, valdocs) # the value's docstring parse_docstring(val_doc, docindex) # the value's variables' docstrings if (isinstance(val_doc, NamespaceDoc) and val_doc.variables not in (None, UNKNOWN)): for var_doc in val_doc.variables.values(): parse_docstring(var_doc, docindex) log.end_progress() # Take care of inheritance. log.start_progress('Inheriting documentation') for i, val_doc in enumerate(valdocs): if isinstance(val_doc, ClassDoc): percent = float(i) / len(valdocs) log.progress(percent, val_doc.canonical_name) inherit_docs(val_doc) log.end_progress() # Initialize the groups & sortedvars attributes. log.start_progress('Sorting & Grouping') for i, val_doc in enumerate(valdocs): if isinstance(val_doc, NamespaceDoc): percent = float(i) / len(valdocs) log.progress(percent, val_doc.canonical_name) val_doc.init_sorted_variables() val_doc.init_variable_groups() if isinstance(val_doc, ModuleDoc): val_doc.init_submodule_groups() log.end_progress() return docindex
def _colorize_re_tree(self, tree, state, noparen, groups): assert noparen in (True, False) if len(tree) > 1 and not noparen: self._output('(', self.RE_GROUP_TAG, state) for elt in tree: op = elt[0] args = elt[1] if op == sre_constants.LITERAL: c = unichr(args) # Add any appropriate escaping. if c in '.^$\\*+?{}[]|()\'': c = '\\'+c elif c == '\t': c = '\\t' elif c == '\r': c = '\\r' elif c == '\n': c = '\\n' elif c == '\f': c = '\\f' elif c == '\v': c = '\\v' elif ord(c) > 0xffff: c = r'\U%08x' % ord(c) elif ord(c) > 0xff: c = r'\u%04x' % ord(c) elif ord(c)<32 or ord(c)>=127: c = r'\x%02x' % ord(c) self._output(c, self.RE_CHAR_TAG, state) elif op == sre_constants.ANY: self._output('.', self.RE_CHAR_TAG, state) elif op == sre_constants.BRANCH: if args[0] is not None: raise ValueError('Branch expected None arg but got %s' % args[0]) for i, item in enumerate(args[1]): if i > 0: self._output('|', self.RE_OP_TAG, state) self._colorize_re_tree(item, state, True, groups) elif op == sre_constants.IN: if (len(args) == 1 and args[0][0] == sre_constants.CATEGORY): self._colorize_re_tree(args, state, False, groups) else: self._output('[', self.RE_GROUP_TAG, state) self._colorize_re_tree(args, state, True, groups) self._output(']', self.RE_GROUP_TAG, state) elif op == sre_constants.CATEGORY: if args == sre_constants.CATEGORY_DIGIT: val = r'\d' elif args == sre_constants.CATEGORY_NOT_DIGIT: val = r'\D' elif args == sre_constants.CATEGORY_SPACE: val = r'\s' elif args == sre_constants.CATEGORY_NOT_SPACE: val = r'\S' elif args == sre_constants.CATEGORY_WORD: val = r'\w' elif args == sre_constants.CATEGORY_NOT_WORD: val = r'\W' else: raise ValueError('Unknown category %s' % args) self._output(val, self.RE_CHAR_TAG, state) elif op == sre_constants.AT: if args == sre_constants.AT_BEGINNING_STRING: val = r'\A' elif args == sre_constants.AT_BEGINNING: val = r'^' elif args == sre_constants.AT_END: val = r'$' elif args == sre_constants.AT_BOUNDARY: val = r'\b' elif args == sre_constants.AT_NON_BOUNDARY: val = r'\B' elif args == sre_constants.AT_END_STRING: val = r'\Z' else: raise ValueError('Unknown position %s' % args) self._output(val, self.RE_CHAR_TAG, state) elif op in (sre_constants.MAX_REPEAT, sre_constants.MIN_REPEAT): minrpt = args[0] maxrpt = args[1] if maxrpt == sre_constants.MAXREPEAT: if minrpt == 0: val = '*' elif minrpt == 1: val = '+' else: val = '{%d,}' % (minrpt) elif minrpt == 0: if maxrpt == 1: val = '?' else: val = '{,%d}' % (maxrpt) elif minrpt == maxrpt: val = '{%d}' % (maxrpt) else: val = '{%d,%d}' % (minrpt, maxrpt) if op == sre_constants.MIN_REPEAT: val += '?' self._colorize_re_tree(args[2], state, False, groups) self._output(val, self.RE_OP_TAG, state) elif op == sre_constants.SUBPATTERN: if args[0] is None: self._output('(?:', self.RE_GROUP_TAG, state) elif args[0] in groups: self._output('(?P<', self.RE_GROUP_TAG, state) self._output(groups[args[0]], self.RE_REF_TAG, state) self._output('>', self.RE_GROUP_TAG, state) elif isinstance(args[0], (int, long)): # This is cheating: self._output('(', self.RE_GROUP_TAG, state) else: self._output('(?P<', self.RE_GROUP_TAG, state) self._output(args[0], self.RE_REF_TAG, state) self._output('>', self.RE_GROUP_TAG, state) self._colorize_re_tree(args[1], state, True, groups) self._output(')', self.RE_GROUP_TAG, state) elif op == sre_constants.GROUPREF: self._output('\\%d' % args, self.RE_REF_TAG, state) elif op == sre_constants.RANGE: self._colorize_re_tree( ((sre_constants.LITERAL, args[0]),), state, False, groups ) self._output('-', self.RE_OP_TAG, state) self._colorize_re_tree( ((sre_constants.LITERAL, args[1]),), state, False, groups ) elif op == sre_constants.NEGATE: self._output('^', self.RE_OP_TAG, state) elif op == sre_constants.ASSERT: if args[0] > 0: self._output('(?=', self.RE_GROUP_TAG, state) else: self._output('(?<=', self.RE_GROUP_TAG, state) self._colorize_re_tree(args[1], state, True, groups) self._output(')', self.RE_GROUP_TAG, state) elif op == sre_constants.ASSERT_NOT: if args[0] > 0: self._output('(?!', self.RE_GROUP_TAG, state) else: self._output('(?<!', self.RE_GROUP_TAG, state) self._colorize_re_tree(args[1], state, True, groups) self._output(')', self.RE_GROUP_TAG, state) elif op == sre_constants.NOT_LITERAL: self._output('[^', self.RE_GROUP_TAG, state) self._colorize_re_tree( ((sre_constants.LITERAL, args),), state, False, groups ) self._output(']', self.RE_GROUP_TAG, state) else: log.error("Error colorizing regexp: unknown elt %r" % elt) if len(tree) > 1 and not noparen: self._output(')', self.RE_GROUP_TAG, state)
log.warning("Finished!") done[0] = "done" except SystemExit: # Cancel. log.error("Cancelled!") done[0] = "cancel" raise except Exception, e: # We failed. log.error("Internal error: %s" % e) done[0] = "cancel" raise except: # We failed. log.error("Internal error!") done[0] = "cancel" raise ##///////////////////////////////////////////////////////////////////////// ## GUI ##///////////////////////////////////////////////////////////////////////// class EpydocGUI: """ A graphical user interace to epydoc. """ def __init__(self):