Thread: [Epydoc-commits] SF.net SVN: epydoc: [1373] branches/exp-args_in_class/epydoc/src/epydoc
Brought to you by:
edloper
From: <dva...@us...> - 2006-09-08 10:39:07
|
Revision: 1373 http://svn.sourceforge.net/epydoc/?rev=1373&view=rev Author: dvarrazzo Date: 2006-09-08 03:38:55 -0700 (Fri, 08 Sep 2006) Log Message: ----------- - Allow parameter-related fields in a class docstring: use them to extend the matching __init__ docstring. - Enforce docstring parsing in dotted name order to ensure class docstring have already been parsed at __init__ docstring parsing time. - "type" fields are used to specify both class/instance variables types and constructor arguments types. In case a type is expressed in both class and __init__ docstring, the latter wins (e.g. the __init__ may receive an arg, parse it and store it in the instance state with the same name but different type) Modified Paths: -------------- branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py branches/exp-args_in_class/epydoc/src/epydoc/docbuilder.py branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py Modified: branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py 2006-09-08 10:28:10 UTC (rev 1372) +++ branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py 2006-09-08 10:38:55 UTC (rev 1373) @@ -1118,6 +1118,10 @@ """@ivar: API documentation for the class's known subclasses. @type: C{list} of L{ClassDoc}""" #} + init_args = UNKNOWN + """@ivar: Tags to be used as constructor documentation. + @type: C{list} of C{(tag, arg, descr)} tuples""" + #} def apidoc_links(self, **filters): val_docs = NamespaceDoc.apidoc_links(self, **filters) Modified: branches/exp-args_in_class/epydoc/src/epydoc/docbuilder.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docbuilder.py 2006-09-08 10:28:10 UTC (rev 1372) +++ branches/exp-args_in_class/epydoc/src/epydoc/docbuilder.py 2006-09-08 10:38:55 UTC (rev 1373) @@ -182,7 +182,10 @@ valdocs = docindex.reachable_valdocs(sort_by_name=True, imports=False, submodules=False, packages=False, subclasses=False) - for i, val_doc in enumerate(valdocs): + # Sort according to dotted name, so __init__ docstrings are parsed after + # class docstrings, allowing constructor parameters to be specified in the + # latters + for i, val_doc in enumerate(sorted(valdocs)): _report_valdoc_progress(i, val_doc, valdocs) # the value's docstring parse_docstring(val_doc, docindex) Modified: branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-08 10:28:10 UTC (rev 1372) +++ branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-08 10:38:55 UTC (rev 1373) @@ -197,6 +197,37 @@ descr, fields = parsed_docstring.split_fields(parse_errors) api_doc.descr = descr + # Handle the constructor fields that have been defined in the class + # docstring. This code assumes that a class docstring is parsed before + # the same class __init__ docstring. + if isinstance(api_doc, ClassDoc): + # Put aside param-related fields, which are to be considered as + # __init__ fields. "type" fields are to be used in both contexts. + param_tags = ('arg', 'argument', 'parameter', 'param', + 'kwarg', 'keyword', 'kwparam') + i = 0 + while i < len(fields): + field = fields[i] + if field.tag() in param_tags: + api_doc.init_args.append(fields.pop(i)) + continue + elif field.tag() == 'type': + api_doc.init_args.append(field) + i += 1 + + elif (isinstance(api_doc, RoutineDoc) + and api_doc.canonical_name[-1] == '__init__'): + # __init__ can receive arguments descriptions from the class docstring + # if a type is specified in both docstring, __init__ wins. + class_doc = docindex.get_valdoc(api_doc.canonical_name[:-1]) + if class_doc is not None and class_doc.init_args is not UNKNOWN: + init_types = [ field.arg() for field in fields + if field.tag() == 'type' ] + for field in class_doc.init_args: + if field.tag() == 'type' and field.arg() in init_types: + continue + fields.append(field) + # Process fields field_warnings = [] for field in fields: @@ -234,6 +265,9 @@ api_doc.summary = None if api_doc.metadata is UNKNOWN: api_doc.metadata = [] + if isinstance(api_doc, ClassDoc): + if api_doc.init_args is UNKNOWN: + api_doc.init_args = [] if isinstance(api_doc, RoutineDoc): if api_doc.arg_descrs is UNKNOWN: api_doc.arg_descrs = [] @@ -586,15 +620,24 @@ api_doc.return_type = descr def process_arg_field(api_doc, docindex, tag, arg, descr): - _check(api_doc, tag, arg, context=RoutineDoc, expect_arg=True) + _check(api_doc, tag, arg, context=(ClassDoc, RoutineDoc), expect_arg=True) idents = re.split('[:;, ] *', arg) - api_doc.arg_descrs.append( (idents, descr) ) - # Check to make sure that the documented parameter(s) are - # actually part of the function signature. - bad_params = ['"%s"' % i for i in idents if i not in api_doc.all_args()] - if bad_params: - raise ValueError(BAD_PARAM % (tag, ', '.join(bad_params))) + if isinstance(api_doc, RoutineDoc): + api_doc.arg_descrs.append( (idents, descr) ) + + # Check to make sure that the documented parameter(s) are + # actually part of the function signature. + bad_params = ['"%s"' % i for i in idents if i not in api_doc.all_args()] + if bad_params: + raise ValueError(BAD_PARAM % (tag, ', '.join(bad_params))) + + elif isinstance(api_doc, ClassDoc): + api_doc.init_args.append( (tag, arg, descr) ) + + else: + assert False, "unexpected context" + def process_kwarg_field(api_doc, docindex, tag, arg, descr): # [xx] these should -not- be checked if they exist.. # and listed separately or not?? This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2006-09-08 15:40:22
|
Revision: 1374 http://svn.sourceforge.net/epydoc/?rev=1374&view=rev Author: dvarrazzo Date: 2006-09-08 08:40:13 -0700 (Fri, 08 Sep 2006) Log Message: ----------- - __init__ signature can be described in the class docstring also when there is no __init__.__doc__ at all. - parse_function_signature() can receive two arguments: one whose docstring is to be parsed and one to be populated. So the constructor signature can be parsed from the class docstring. - Dropped generation of variables when there is a type w/o matching var. The issue is still to be defined consistently anyway. Modified Paths: -------------- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py Added Paths: ----------- branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest Modified: branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-08 10:38:55 UTC (rev 1373) +++ branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-08 15:40:13 UTC (rev 1374) @@ -174,8 +174,12 @@ initialize_api_doc(api_doc) # If there's no docstring, then there's nothing more to do. + # ...except in a case: an __init__ function that is to receive some + # documentation from the class docstring if (api_doc.docstring in (None, UNKNOWN)): - return + if not (isinstance(api_doc, RoutineDoc) + and api_doc.canonical_name[-1] == '__init__'): + return # Remove leading indentation from the docstring. api_doc.docstring = unindent_docstring(api_doc.docstring) @@ -184,6 +188,10 @@ # overrides any signature we got via introspection/parsing. if isinstance(api_doc, RoutineDoc): parse_function_signature(api_doc) + elif isinstance(api_doc, ClassDoc): + initvar = api_doc.variables.get('__init__') + if initvar: + parse_function_signature(initvar.value, api_doc) # Parse the docstring. Any errors encountered are stored as # `ParseError` objects in the errors list. @@ -694,9 +702,16 @@ def set_var_type(api_doc, ident, descr): if ident not in api_doc.variables: - api_doc.variables[ident] = VariableDoc( - container=api_doc, name=ident, - canonical_name=api_doc.canonical_name+ident) + # [xx] If there is a type w/o matching var, this would create + # a new var. The behavior is to be decied consistently also in other + # places in this sources (grep for [xx]). + # Currently disable the creation or else each "type" used in the + # class docstring to describe an __init__ parameter also generates + # an extra class variable. + #api_doc.variables[ident] = VariableDoc( + #container=api_doc, name=ident, + #canonical_name=api_doc.canonical_name+ident) + return var_doc = api_doc.variables[ident] if var_doc.type_descr not in (None, UNKNOWN): @@ -740,7 +755,7 @@ # [xx] copied from inspect.getdoc(); we can't use inspect.getdoc() # itself, since it expects an object, not a string. - if docstring == '': return '' + if not docstring: return '' lines = docstring.expandtabs().split('\n') # Find minimum indentation of any non-blank lines after first line. @@ -823,7 +838,7 @@ """A regular expression that is used to extract signatures from docstrings.""" -def parse_function_signature(func_doc): +def parse_function_signature(func_doc, doc_source=None): """ Construct the signature for a builtin function or method from its docstring. If the docstring uses the standard convention @@ -833,15 +848,26 @@ Otherwise, the signature will be set to a single varargs variable named C{"..."}. + @param func_doc: The target object where to store parsed signature. Also + container of the docstring to parse if doc_source is C{None} + @type L{RoutineDoc} + @param doc_source: Contains the docstring to parse. If C{None}, parse + L{func_doc} docstring instead + @type L{APIDoc} @rtype: C{None} """ + if doc_source is None: + doc_source = func_doc + # If there's no docstring, then don't do anything. - if not func_doc.docstring: return False + if not doc_source.docstring: return False - m = _SIGNATURE_RE.match(func_doc.docstring) + m = _SIGNATURE_RE.match(doc_source.docstring) if m is None: return False # Do I want to be this strict? + # Notice that __init__ must match the class name instead, if the signature + # comes from the class docstring # if not (m.group('func') == func_doc.canonical_name[-1] or # '_'+m.group('func') == func_doc.canonical_name[-1]): # log.warning("Not extracting function signature from %s's " @@ -900,7 +926,7 @@ func_doc.posarg_defaults.insert(0, None) # Remove the signature from the docstring. - func_doc.docstring = func_doc.docstring[m.end():] + doc_source.docstring = doc_source.docstring[m.end():] # We found a signature. return True Added: branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest (rev 0) +++ branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest 2006-09-08 15:40:13 UTC (rev 1374) @@ -0,0 +1,171 @@ +Regression Testing for epydoc.docbuilder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Test Function +============= +This test function takes a string containing the contents of a module. +It writes the string contents to a file, imports the file as a module, +and uses build_doc to build documentation, and pretty prints the resulting +ModuleDoc object. The `attribs` argument specifies which attributes +of the `APIDoc`s should be displayed. The `introspect` argument gives the +name of a variable in the module whose value should be introspected, +instead of introspecting the whole module. + + >>> import tempfile, re, os, os.path, textwrap, sys + >>> from epydoc.docbuilder import build_doc + >>> def runbuilder(s, attribs='', introspect=None, exclude=''): + ... # Write it to a temp file. + ... tmp_dir = tempfile.mkdtemp() + ... out = open(os.path.join(tmp_dir, 'epydoc_test.py'), 'w') + ... out.write(textwrap.dedent(s)) + ... out.close() + ... # Import it. + ... sys.path.insert(0, tmp_dir) + ... if introspect is None: + ... import epydoc_test as val + ... else: + ... exec("from epydoc_test import %s as val" % introspect) + ... del sys.path[0] + ... # Introspect it. + ... val_doc = build_doc(val) + ... # Display it. + ... s = val_doc.pp(include=attribs.split(),exclude=exclude.split()) + ... s = re.sub(r"(filename = ).*", r"\1...", s) + ... s = re.sub(r"(<module 'epydoc_test' from ).*", r'\1...', s) + ... s = re.sub(r"(<function \w+ at )0x\w+>", r"\1...>", s) + ... s = re.sub(r"(<\w+ object at )0x\w+>", r"\1...>", s) + ... print s + ... # Clean up. + ... os.unlink(os.path.join(tmp_dir, 'epydoc_test.py')) + ... try: os.unlink(os.path.join(tmp_dir, 'epydoc_test.pyc')) + ... except OSError: pass + ... os.rmdir(tmp_dir) + ... del sys.modules['epydoc_test'] + + +Specifying constructor signature in class docstring +=================================================== + +The class signature can be specified in the class docstring instead of __init__ + + >>> runbuilder(s=''' + ... class Foo: + ... """This is the object docstring + ... + ... @param a: init param. + ... @ivar a: instance var. + ... @type a: date + ... """ + ... def __init__(self, a): + ... """The ctor docstring. + ... + ... @type a: str + ... """ + ... pass + ... ''', + ... introspect="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + | +- name = '__init__' + | +- value + | +- RoutineDoc for epydoc_test.Foo.__init__ [2] + | +- arg_descrs = [([u'a'], ... + | +- arg_types = {u'a': ... + | +- kwarg = None + | +- posargs = ['self', 'a'] + | +- vararg = None + +- a => VariableDoc for epydoc_test.Foo.a [3] + +- name = u'a' + +- value = <UNKNOWN> + +Also keywords arguments can be put in the constructor + + >>> runbuilder(s=''' + ... class Foo: + ... """This is the object docstring + ... + ... @keyword a: a kwarg. + ... @type a: str + ... """ + ... def __init__(self, **kwargs): + ... """The ctor docstring. + ... + ... @type a: str + ... """ + ... ''', + ... introspect="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + +- name = '__init__' + +- value + +- RoutineDoc for epydoc_test.Foo.__init__ [2] + +- arg_descrs = [([u'a'], ... + +- arg_types = {u'a': ... + +- kwarg = 'kwargs' + +- posargs = ['self'] + +- vararg = None + +A missing docstring on the __init__ is not an issue. + >>> runbuilder(s=''' + ... class Foo: + ... """This is the object docstring + ... + ... @param a: a param. + ... @type a: str + ... """ + ... def __init__(self): + ... pass + ... ''', + ... introspect="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + +- name = '__init__' + +- value + +- RoutineDoc for epydoc_test.Foo.__init__ [2] + +- arg_descrs = [([u'a'], ... + +- arg_types = {u'a': ... + +- kwarg = None + +- posargs = ['self'] + +- vararg = None + +Epydoc can also grok the constructor signature from the class docstring + + >>> runbuilder(s=''' + ... class Foo: + ... """Foo(x, y) + ... + ... A class to ship rockets in outer space. + ... + ... @param x: first param + ... @param y: second param + ... """ + ... def __init__(self, a, b): + ... """__init__ doc""" + ... pass + ... ''', + ... introspect="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type arg_types arg_descrs docstring") + ClassDoc for epydoc_test.Foo [0] + +- docstring = u'A class to ship rockets ... + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + +- docstring = <UNKNOWN> + +- name = '__init__' + +- value + +- RoutineDoc for epydoc_test.Foo.__init__ [2] + +- arg_descrs = [([u'x'], ... + +- arg_types = {} + +- docstring = u'__init__ doc' + +- kwarg = None + +- posargs = [u'x', u'y'] + +- vararg = None This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2006-09-15 01:48:02
|
Revision: 1390 http://svn.sourceforge.net/epydoc/?rev=1390&view=rev Author: dvarrazzo Date: 2006-09-14 18:47:53 -0700 (Thu, 14 Sep 2006) Log Message: ----------- - Checked in a new algorithm to split fields in the class docstring between class documentation and constructor documentation. The algorithm requires the `type` fields to appear after the related fields. - Added more test. Modified Paths: -------------- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest Modified: branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-14 21:37:38 UTC (rev 1389) +++ branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-15 01:47:53 UTC (rev 1390) @@ -205,39 +205,75 @@ descr, fields = parsed_docstring.split_fields(parse_errors) api_doc.descr = descr + field_warnings = [] + # Handle the constructor fields that have been defined in the class # docstring. This code assumes that a class docstring is parsed before # the same class __init__ docstring. if isinstance(api_doc, ClassDoc): - # Put aside param-related fields, which are to be considered as - # __init__ fields. "type" fields are to be used in both contexts. - param_tags = ('arg', 'argument', 'parameter', 'param', - 'kwarg', 'keyword', 'kwparam') + + # Split fields in lists according to their argument + arg_fields = {} + args_order = [] i = 0 while i < len(fields): field = fields[i] - if field.tag() in param_tags: - api_doc.init_args.append(fields.pop(i)) + + # gather together all the fields with the same arg + if field.arg() is not None: + arg_fields.setdefault(field.arg(), []).append(fields.pop(i)) + args_order.append(field.arg()) + else: + i += 1 + + # Now check that for each argument there is at most a single variable + # and a single argument, and a single type for them. + for arg in args_order: + ff = arg_fields.pop(arg, None) + if ff is None: continue - elif field.tag() == 'type': - api_doc.init_args.append(field) - i += 1 + var = tvar = par = tpar = None + for field in ff: + if field.tag() in VAR_TAGS: + if var is None: + var = field + fields.append(field) + else: + field_warnings.append("there are more variables " + "named '%s'" % arg) + elif field.tag() in PARAM_TAGS: + if par is None: + par = field + api_doc.init_args.append(field) + else: + field_warnings.append("there are more parameters " + "named '%s'" % arg) + + elif field.tag() == 'type': + gone = False + if var is not None and tvar is None: + tvar = field + fields.append(field) + gone = True + if par is not None and tpar is None: + tpar = field + api_doc.init_args.append(field) + gone = True + if not gone: + field_warnings.append("type for '%s' doesn't apply " + "to any variable or argument" % arg) + + else: # Unespected field + fields.append(field) + elif (isinstance(api_doc, RoutineDoc) - and api_doc.canonical_name[-1] == '__init__'): - # __init__ can receive arguments descriptions from the class docstring - # if a type is specified in both docstring, __init__ wins. + and api_doc.canonical_name[-1] == '__init__'): class_doc = docindex.get_valdoc(api_doc.canonical_name[:-1]) if class_doc is not None and class_doc.init_args is not UNKNOWN: - init_types = [ field.arg() for field in fields - if field.tag() == 'type' ] - for field in class_doc.init_args: - if field.tag() == 'type' and field.arg() in init_types: - continue - fields.append(field) + fields.extend(class_doc.init_args) # Process fields - field_warnings = [] for field in fields: try: process_field(api_doc, docindex, field.tag(), @@ -671,6 +707,14 @@ register_field_handler(process_raise_field, 'raise', 'raises', 'except', 'exception') +# Tags related to function parameters +PARAM_TAGS = ('arg', 'argument', 'parameter', 'param', + 'kwarg', 'keyword', 'kwparam') + +# Tags related to variables in a class +VAR_TAGS = ('cvar', 'cvariable', + 'ivar', 'ivariable') + ###################################################################### #{ Helper Functions ###################################################################### Modified: branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest 2006-09-14 21:37:38 UTC (rev 1389) +++ branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest 2006-09-15 01:47:53 UTC (rev 1390) @@ -14,6 +14,22 @@ >>> import tempfile, re, os, os.path, textwrap, sys >>> from epydoc.docbuilder import build_doc + >>> from epydoc.apidoc import ClassDoc, RoutineDoc + >>> from epydoc.markup import ParsedDocstring + + >>> def to_plain(docstring): + ... """Conver a parsed docstring into plain text""" + ... if isinstance(docstring, ParsedDocstring): + ... docstring = docstring.to_plaintext(None) + ... return docstring.rsplit() + + >>> def fun_to_plain(val_doc): + ... """Convert parsed docstrings in text from a RoutineDoc""" + ... for k, v in val_doc.arg_types.items(): + ... val_doc.arg_types[k] = to_plain(v) + ... for i, (k, v) in enumerate(val_doc.arg_descrs): + ... val_doc.arg_descrs[i] = (k, to_plain(v)) + >>> def runbuilder(s, attribs='', build=None, exclude=''): ... # Write it to a temp file. ... tmp_dir = tempfile.mkdtemp() @@ -24,6 +40,10 @@ ... val_doc = build_doc(os.path.join(tmp_dir, 'epydoc_test.py')) ... if build: val_doc = val_doc.variables[build].value ... # Display it. + ... if isinstance(val_doc, ClassDoc): + ... for val in val_doc.variables.values(): + ... if isinstance(val.value, RoutineDoc): + ... fun_to_plain(val.value) ... s = val_doc.pp(include=attribs.split(),exclude=exclude.split()) ... s = re.sub(r"(filename = ).*", r"\1...", s) ... s = re.sub(r"(<module 'epydoc_test' from ).*", r'\1...', s) @@ -116,11 +136,18 @@ +- descr = u'A property has no defining module' +- type_descr = u'int' +Stuff from future doesn't appear as variable. + >>> runbuilder(s=""" ... from __future__ import division ... from re import match ... """, ... attribs='variables value') + ModuleDoc for epydoc_test [0] + +- variables + +- match => VariableDoc for epydoc_test.match [1] + +- value + +- ValueDoc for sre.match [2] Specifying constructor signature in class docstring @@ -143,7 +170,7 @@ ... """ ... pass ... ''', - ... introspect="Foo", + ... build="Foo", ... attribs="variables name value " ... "posargs vararg kwarg type arg_types arg_descrs") ClassDoc for epydoc_test.Foo [0] @@ -176,7 +203,7 @@ ... @type a: str ... """ ... ''', - ... introspect="Foo", + ... build="Foo", ... attribs="variables name value " ... "posargs vararg kwarg type arg_types arg_descrs") ClassDoc for epydoc_test.Foo [0] @@ -192,6 +219,7 @@ +- vararg = None A missing docstring on the __init__ is not an issue. + >>> runbuilder(s=''' ... class Foo: ... """This is the object docstring @@ -202,7 +230,7 @@ ... def __init__(self): ... pass ... ''', - ... introspect="Foo", + ... build="Foo", ... attribs="variables name value " ... "posargs vararg kwarg type arg_types arg_descrs") ClassDoc for epydoc_test.Foo [0] @@ -232,7 +260,7 @@ ... """__init__ doc""" ... pass ... ''', - ... introspect="Foo", + ... build="Foo", ... attribs="variables name value " ... "posargs vararg kwarg type arg_types arg_descrs docstring") ClassDoc for epydoc_test.Foo [0] @@ -249,3 +277,108 @@ +- kwarg = None +- posargs = [u'x', u'y'] +- vararg = None + +A type can apply to both a param and a variable + + >>> runbuilder(s=''' + ... class Foo: + ... """This is the object docstring + ... + ... @param a: init param. + ... @ivar a: instance var. + ... @type a: date + ... """ + ... def __init__(self, a): + ... pass + ... ''', + ... build="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type_descr arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + | +- name = '__init__' + | +- type_descr = None + | +- value + | +- RoutineDoc for epydoc_test.Foo.__init__ [2] + | +- arg_descrs = [([u'a'], [u'init', u'param.'])] + | +- arg_types = {u'a': [u'date']} + | +- kwarg = None + | +- posargs = ['self', 'a'] + | +- vararg = None + +- a => VariableDoc for epydoc_test.Foo.a [3] + +- name = u'a' + +- type_descr = u'date\n\n' + +- value = <UNKNOWN> + +But there can also be two different types + + >>> runbuilder(s=''' + ... class Foo: + ... """This is the object docstring + ... + ... @param a: init param. + ... @type a: string + ... @ivar a: instance var. + ... @type a: date + ... """ + ... def __init__(self, a): + ... pass + ... ''', + ... build="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type_descr arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + | +- name = '__init__' + | +- type_descr = None + | +- value + | +- RoutineDoc for epydoc_test.Foo.__init__ [2] + | +- arg_descrs = [([u'a'], [u'init', u'param.'])] + | +- arg_types = {u'a': [u'string']} + | +- kwarg = None + | +- posargs = ['self', 'a'] + | +- vararg = None + +- a => VariableDoc for epydoc_test.Foo.a [3] + +- name = u'a' + +- type_descr = u'date\n\n' + +- value = <UNKNOWN> + +Also reST consolidated fields are not a problem. + + >>> runbuilder(s=''' + ... __docformat__ = 'restructuredtext' + ... class Foo: + ... """This is the object docstring + ... + ... :Parameters: + ... `a` : string + ... init param. + ... + ... :IVariables: + ... `a` : date + ... instance var. + ... """ + ... def __init__(self, a): + ... pass + ... ''', + ... build="Foo", + ... attribs="variables name value " + ... "posargs vararg kwarg type_descr arg_types arg_descrs") + ClassDoc for epydoc_test.Foo [0] + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + | +- name = '__init__' + | +- type_descr = None + | +- value + | +- RoutineDoc for epydoc_test.Foo.__init__ [2] + | +- arg_descrs = [([u'a'], [u'init', u'param.'])] + | +- arg_types = {u'a': [u'string']} + | +- kwarg = None + | +- posargs = ['self', 'a'] + | +- vararg = None + +- a => VariableDoc for epydoc_test.Foo.a [3] + +- name = u'a' + +- type_descr = u'date' + +- value = <UNKNOWN> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2006-09-16 00:02:18
|
Revision: 1393 http://svn.sourceforge.net/epydoc/?rev=1393&view=rev Author: dvarrazzo Date: 2006-09-15 17:02:10 -0700 (Fri, 15 Sep 2006) Log Message: ----------- - Reverted updates to `process_arg_field()` handler: is context is always a function, not the class docstring. - Exceptions are handled in the class docstring as well. - More explicit name of some variables. Modified Paths: -------------- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest Modified: branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-15 23:36:23 UTC (rev 1392) +++ branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-16 00:02:10 UTC (rev 1393) @@ -308,7 +308,7 @@ var = tvar = par = tpar = None for field in ff: - if field.tag() in VAR_TAGS: + if field.tag() in VARIABLE_TAGS: if var is None: var = field fields.append(field) @@ -316,7 +316,7 @@ warnings.append( "There is more than one variable named '%s'" % arg) - elif field.tag() in PARAM_TAGS: + elif field.tag() in PARAMETER_TAGS: if par is None: par = field init_fields.append(field) @@ -335,6 +335,9 @@ if par is not None and tpar is None: tpar = field + elif field.tag() in EXCEPTION_TAGS: + init_fields.append(field) + else: # Unespected field fields.append(field) @@ -683,24 +686,15 @@ api_doc.return_type = descr def process_arg_field(api_doc, docindex, tag, arg, descr): - _check(api_doc, tag, arg, context=(ClassDoc, RoutineDoc), expect_arg=True) + _check(api_doc, tag, arg, context=RoutineDoc, expect_arg=True) idents = re.split('[:;, ] *', arg) + api_doc.arg_descrs.append( (idents, descr) ) + # Check to make sure that the documented parameter(s) are + # actually part of the function signature. + bad_params = ['"%s"' % i for i in idents if i not in api_doc.all_args()] + if bad_params: + raise ValueError(BAD_PARAM % (tag, ', '.join(bad_params))) - if isinstance(api_doc, RoutineDoc): - api_doc.arg_descrs.append( (idents, descr) ) - - # Check to make sure that the documented parameter(s) are - # actually part of the function signature. - bad_params = ['"%s"' % i for i in idents if i not in api_doc.all_args()] - if bad_params: - raise ValueError(BAD_PARAM % (tag, ', '.join(bad_params))) - - elif isinstance(api_doc, ClassDoc): - api_doc.init_args.append( (tag, arg, descr) ) - - else: - assert False, "unexpected context" - def process_kwarg_field(api_doc, docindex, tag, arg, descr): # [xx] these should -not- be checked if they exist.. # and listed separately or not?? @@ -727,13 +721,15 @@ 'except', 'exception') # Tags related to function parameters -PARAM_TAGS = ('arg', 'argument', 'parameter', 'param', - 'kwarg', 'keyword', 'kwparam') +PARAMETER_TAGS = ('arg', 'argument', 'parameter', 'param', + 'kwarg', 'keyword', 'kwparam') # Tags related to variables in a class -VAR_TAGS = ('cvar', 'cvariable', - 'ivar', 'ivariable') +VARIABLE_TAGS = ('cvar', 'cvariable', 'ivar', 'ivariable') +# Tags related to exceptions +EXCEPTION_TAGS = ('raise', 'raises', 'except', 'exception') + ###################################################################### #{ Helper Functions ###################################################################### Modified: branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest 2006-09-15 23:36:23 UTC (rev 1392) +++ branches/exp-args_in_class/epydoc/src/epydoc/test/docbuilder.doctest 2006-09-16 00:02:10 UTC (rev 1393) @@ -245,6 +245,42 @@ +- posargs = ['self'] +- vararg = None +Exceptions can be put in the docstring class, and they are assigned to the +constructor too. + >>> runbuilder(s=''' + ... class Foo: + ... """Foo(x, y) + ... + ... A class to ship rockets in outer space. + ... + ... @param x: first param + ... @param y: second param + ... @except ValueError: frobnication error + ... """ + ... def __init__(self, a, b): + ... """__init__ doc""" + ... pass + ... ''', + ... build="Foo", + ... attribs="variables name value exception_descrs " + ... "posargs vararg kwarg type arg_types arg_descrs docstring") + ClassDoc for epydoc_test.Foo [0] + +- docstring = u'A class to ship rockets in outer sp... + +- variables + +- __init__ => VariableDoc for epydoc_test.Foo.__init__ [1] + +- docstring = <UNKNOWN> + +- name = '__init__' + +- value + +- RoutineDoc for epydoc_test.Foo.__init__ [2] + +- arg_descrs = [([u'x'], [u'first', u'param']), ... + +- arg_types = {} + +- docstring = u'__init__ doc' + +- exception_descrs = [(DottedName(u'ValueError'), ... + +- kwarg = None + +- posargs = [u'x', u'y'] + +- vararg = None + + Epydoc can also grok the constructor signature from the class docstring >>> runbuilder(s=''' @@ -356,6 +392,10 @@ ... `a` : string ... init param. ... + ... :Exceptions: + ... * `ValueError`: frobnication error + ... init param. + ... ... :IVariables: ... `a` : date ... instance var. @@ -364,7 +404,7 @@ ... pass ... ''', ... build="Foo", - ... attribs="variables name value " + ... attribs="variables name value exception_descrs " ... "posargs vararg kwarg type_descr arg_types arg_descrs") ClassDoc for epydoc_test.Foo [0] +- variables @@ -375,6 +415,7 @@ | +- RoutineDoc for epydoc_test.Foo.__init__ [2] | +- arg_descrs = [([u'a'], [u'init', u'param.'])] | +- arg_types = {u'a': [u'string']} + | +- exception_descrs = [(DottedName(u'ValueError'), ... | +- kwarg = None | +- posargs = ['self', 'a'] | +- vararg = None This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2006-09-16 12:57:21
|
Revision: 1395 http://svn.sourceforge.net/epydoc/?rev=1395&view=rev Author: dvarrazzo Date: 2006-09-16 05:57:14 -0700 (Sat, 16 Sep 2006) Log Message: ----------- - Class docstring fields are passed to __init__ without using an extra ClassDoc attribute. - Some docstring fixed. Modified Paths: -------------- branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py Modified: branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py 2006-09-16 00:20:06 UTC (rev 1394) +++ branches/exp-args_in_class/epydoc/src/epydoc/apidoc.py 2006-09-16 12:57:14 UTC (rev 1395) @@ -1118,10 +1118,6 @@ """@ivar: API documentation for the class's known subclasses. @type: C{list} of L{ClassDoc}""" #} - init_args = UNKNOWN - """@ivar: Tags to be used as constructor documentation. - @type: C{list} of C{(tag, arg, descr)} tuples""" - #} def apidoc_links(self, **filters): val_docs = NamespaceDoc.apidoc_links(self, **filters) Modified: branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py =================================================================== --- branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-16 00:20:06 UTC (rev 1394) +++ branches/exp-args_in_class/epydoc/src/epydoc/docstringparser.py 2006-09-16 12:57:14 UTC (rev 1395) @@ -168,18 +168,16 @@ user docfields defined by containing objects. """ if api_doc.metadata is not UNKNOWN: - log.debug("%s's docstring processed twice" % api_doc.canonical_name) + if not (isinstance(api_doc, RoutineDoc) + and api_doc.canonical_name[-1] == '__init__'): + log.debug("%s's docstring processed twice" % api_doc.canonical_name) return initialize_api_doc(api_doc) # If there's no docstring, then there's nothing more to do. - # ...except in a case: an __init__ function that is to receive some - # documentation from the class docstring if (api_doc.docstring in (None, UNKNOWN)): - if not (isinstance(api_doc, RoutineDoc) - and api_doc.canonical_name[-1] == '__init__'): - return + return # Remove leading indentation from the docstring. api_doc.docstring = unindent_docstring(api_doc.docstring) @@ -188,10 +186,6 @@ # overrides any signature we got via introspection/parsing. if isinstance(api_doc, RoutineDoc): parse_function_signature(api_doc) - elif isinstance(api_doc, ClassDoc): - initvar = api_doc.variables.get('__init__') - if initvar: - parse_function_signature(initvar.value, api_doc) # Parse the docstring. Any errors encountered are stored as # `ParseError` objects in the errors list. @@ -211,14 +205,23 @@ # docstring. This code assumes that a class docstring is parsed before # the same class __init__ docstring. if isinstance(api_doc, ClassDoc): - split_init_fields(fields, api_doc.init_args, field_warnings) - elif (isinstance(api_doc, RoutineDoc) - and api_doc.canonical_name[-1] == '__init__'): - class_doc = docindex.get_valdoc(api_doc.canonical_name[:-1]) - if class_doc is not None and class_doc.init_args is not UNKNOWN: - fields.extend(class_doc.init_args) + # Parse ahead the __init__ docstring for this class + initvar = api_doc.variables.get('__init__') + if initvar and initvar.value not in (None, UNKNOWN): + init_api_doc = initvar.value + parse_docstring(init_api_doc, docindex) + parse_function_signature(init_api_doc, api_doc) + init_fields = split_init_fields(fields, field_warnings) + + # Process fields + for field in init_fields: + try: + process_field(init_api_doc, docindex, field.tag(), + field.arg(), field.body()) + except ValueError, e: field_warnings.append(str(e)) + # Process fields for field in fields: try: @@ -255,9 +258,6 @@ api_doc.summary = None if api_doc.metadata is UNKNOWN: api_doc.metadata = [] - if isinstance(api_doc, ClassDoc): - if api_doc.init_args is UNKNOWN: - api_doc.init_args = [] if isinstance(api_doc, RoutineDoc): if api_doc.arg_descrs is UNKNOWN: api_doc.arg_descrs = [] @@ -278,13 +278,20 @@ if api_doc.sort_spec is UNKNOWN: api_doc.sort_spec = [] -def split_init_fields(fields, init_fields, warnings): +def split_init_fields(fields, warnings): """ - Move the fields related to init into a different list. - C{fields} is supposed obtained from a class docstring. Remove - fields not related to the class from it and add them to C{init_fields}, - which is a list of constructor fields. + Remove the fields related to the constructor from a class docstring + fields list. + + @param fields: The fields to process. The list will be modified in place + @type fields: C{list} of L{markup.Field} + @param warnings: A list to emit processing warnings + @type warnings: C{list} + @return: The C{fields} items to be applied to the C{__init__} method + @rtype: C{list} of L{markup.Field} """ + init_fields = [] + # Split fields in lists according to their argument, keeping order. arg_fields = {} args_order = [] @@ -354,6 +361,8 @@ else: pass # [xx] warn about type w/o object? + return init_fields + def report_errors(api_doc, docindex, parse_errors, field_warnings): """A helper function for L{parse_docstring()} that reports any markup warnings and field warnings that we encountered while @@ -902,10 +911,10 @@ @param func_doc: The target object where to store parsed signature. Also container of the docstring to parse if doc_source is C{None} - @type L{RoutineDoc} + @type func_doc: L{RoutineDoc} @param doc_source: Contains the docstring to parse. If C{None}, parse L{func_doc} docstring instead - @type L{APIDoc} + @type doc_source: L{APIDoc} @rtype: C{None} """ if doc_source is None: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |