summaryrefslogtreecommitdiff
path: root/docutils/readers/python
diff options
context:
space:
mode:
authorgoodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2002-12-18 01:44:49 +0000
committergoodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2002-12-18 01:44:49 +0000
commit173c7a539ca4e2b3a9f8c3a06f67d4a868fd850c (patch)
tree436e1eae9f88be159c3640568546b43ee36618b7 /docutils/readers/python
parentae5fd4a5e4f398a38b52b712b4a19ad8416139e3 (diff)
downloaddocutils-173c7a539ca4e2b3a9f8c3a06f67d4a868fd850c.tar.gz
More progress; functions done.
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk/docutils@1027 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'docutils/readers/python')
-rw-r--r--docutils/readers/python/moduleparser.py197
1 files changed, 157 insertions, 40 deletions
diff --git a/docutils/readers/python/moduleparser.py b/docutils/readers/python/moduleparser.py
index cbca876a7..8dcf432b2 100644
--- a/docutils/readers/python/moduleparser.py
+++ b/docutils/readers/python/moduleparser.py
@@ -163,6 +163,14 @@ It's for exactly this reason:
... but it goes past docstring processing. It's also important to keep style
decisions and tool-specific data transforms out of this module parser.
+
+Issues
+======
+
+* At what point should namespaces be computed? Should they be part of the
+ basic AST produced by the ASTVisitor walk, or generated by another tree
+ traversal?
+
"""
__docformat__ = 'reStructuredText'
@@ -174,7 +182,7 @@ import tokenize
import token
from compiler.consts import OP_ASSIGN
from compiler.visitor import ASTVisitor
-from types import StringType, UnicodeType
+from types import StringType, UnicodeType, TupleType
def parse_module(module_text, filename):
@@ -304,7 +312,13 @@ class AttributeVisitor(BaseVisitor):
class FunctionVisitor(DocstringVisitor):
+ in_function = 0
+
def visitFunction(self, node):
+ if self.in_function:
+ # Don't bother with nested function definitions.
+ return
+ self.in_function = 1
self.function = function = Function(node, node.name)
if node.doc is not None:
function.append(Docstring(node, node.doc))
@@ -326,11 +340,17 @@ class FunctionVisitor(DocstringVisitor):
argnames.pop()
defaults = list(node.defaults)
defaults = [None] * (len(argnames) - len(defaults)) + defaults
+ function_parameters = self.token_parser.function_parameters(
+ node.lineno)
+ #print >>sys.stderr, function_parameters
for argname, default in zip(argnames, defaults):
- parameter = Parameter(node, argname)
+ if type(argname) is TupleType:
+ parameter = ParameterTuple(node, argname)
+ argname = normalize_parameter_name(argname)
+ else:
+ parameter = Parameter(node, argname)
if default:
- default_text = self.token_parser.default(node.lineno)
- parameter.append(Default(node, default_text))
+ parameter.append(Default(node, function_parameters[argname]))
parameters.append(parameter)
if parameters or special:
special.reverse()
@@ -463,6 +483,12 @@ class ParameterList(Node): pass
class Parameter(Attribute): pass
+class ParameterTuple(AttributeTuple):
+
+ def attlist(self):
+ return Node.attlist(self, names=normalize_parameter_name(self.names))
+
+
class ExcessPositionalArguments(Parameter): pass
@@ -502,47 +528,129 @@ class TokenParser:
while self.string != '=':
self.next()
while self.type != token.NEWLINE and self.string != ';':
- append = 1
- append_ws = 1
- del_ws = 0
if self.string == '=':
- start_row, start_col = self.end
- tokens = []
- last_type = None
- last_string = None
- backquote = 0
- append = 0
- elif self.string == '.':
- del_ws = 1
- append_ws = 0
- elif self.string in ('(', '[', '{'):
- append_ws = 0
- if self.string in '([' and (last_type == token.NAME or
- last_string in (')', ']', '}')):
- del_ws = 1
- elif self.string in (')', ']', '}', ':', ','):
- del_ws = 1
- elif self.string == '`':
- if backquote:
- del_ws = 1
- else:
- append_ws = 0
- backquote = not backquote
- elif self.type == tokenize.NL:
- append = 0
- if append:
- if del_ws and tokens and tokens[-1] == ' ':
- del tokens[-1]
- tokens.append(self.string)
- last_type = self.type
- last_string = self.string
- if append_ws:
- tokens.append(' ')
+ self.tokens = []
+ self.stack = []
+ self._type = None
+ self._string = None
+ self._backquote = 0
+ else:
+ self.note_token()
self.next()
self.next()
- text = ''.join(tokens)
+ text = ''.join(self.tokens)
return text.strip()
+ openers = {')': '(', ']': '[', '}': '{'}
+
+ def note_token(self):
+ append = 1
+ append_ws = 1
+ del_ws = 0
+ if self.string == '.':
+ del_ws = 1
+ append_ws = 0
+ elif self.string in ('(', '[', '{'):
+ append_ws = 0
+ if self.string in '([' and (self._type == token.NAME or
+ self._string in (')', ']', '}')):
+ del_ws = 1
+ self.stack.append(self.string)
+ elif self.string in (')', ']', '}'):
+ del_ws = 1
+ assert self.stack[-1] == self.openers[self.string]
+ self.stack.pop()
+ elif self.string in (':', ','):
+ del_ws = 1
+ elif self.string == '`':
+ if self._backquote:
+ del_ws = 1
+ assert self.stack[-1] == self.string
+ self.stack.pop()
+ else:
+ append_ws = 0
+ self.stack.append(self.string)
+ self._backquote = not self._backquote
+ elif self.type == tokenize.NL:
+ append = 0
+ if append:
+ if del_ws and self.tokens and self.tokens[-1] == ' ':
+ del self.tokens[-1]
+ self.tokens.append(self.string)
+ self._type = self.type
+ self._string = self.string
+ if append_ws:
+ self.tokens.append(' ')
+
+ def function_parameters(self, lineno):
+ """
+ Return a dictionary mapping parameters to defaults
+ (whitespace-normalized strings).
+ """
+ self.goto_line(lineno)
+ while self.string != 'def':
+ self.next()
+ while self.string != '(':
+ self.next()
+ name = None
+ default = None
+ parameter_tuple = None
+ self.tokens = []
+ parameters = {}
+ self.stack = [self.string]
+ self.next()
+ while 1:
+ if len(self.stack) == 1:
+ if parameter_tuple:
+ # Just encountered ")".
+ #print >>sys.stderr, 'parameter_tuple: %r' % self.tokens
+ name = ''.join(self.tokens).strip()
+ self.tokens = []
+ parameter_tuple = None
+ if self.string in (')', ','):
+ if name:
+ if self.tokens:
+ default_text = ''.join(self.tokens).strip()
+ else:
+ default_text = None
+ parameters[name] = default_text
+ self.tokens = []
+ name = None
+ default = None
+ if self.string == ')':
+ break
+ elif self.type == token.NAME:
+ if name and default:
+ self.note_token()
+ else:
+ assert name is None, (
+ 'token=%r name=%r parameters=%r stack=%r'
+ % (self.token, name, parameters, self.stack))
+ name = self.string
+ #print >>sys.stderr, 'name=%r' % name
+ elif self.string == '=':
+ assert name is not None, 'token=%r' % (self.token,)
+ assert default is None, 'token=%r' % (self.token,)
+ assert self.tokens == [], 'token=%r' % (self.token,)
+ default = 1
+ self._type = None
+ self._string = None
+ self._backquote = 0
+ elif name:
+ self.note_token()
+ elif self.string == '(':
+ parameter_tuple = 1
+ self._type = None
+ self._string = None
+ self._backquote = 0
+ self.note_token()
+ else: # ignore these tokens:
+ assert self.string in ('*', '**', '\n'), (
+ 'token=%r' % (self.token,))
+ else:
+ self.note_token()
+ self.next()
+ return parameters
def trim_docstring(text):
"""
@@ -573,3 +681,12 @@ def trim_docstring(text):
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
+
+def normalize_parameter_name(name):
+ """
+ Converts a tuple like ``('a', ('b', 'c'), 'd')`` into ``'(a, (b, c), d)'``
+ """
+ if type(name) is TupleType:
+ return '(%s)' % ', '.join([normalize_parameter_name(n) for n in name])
+ else:
+ return name