Add static method support.

This commit is contained in:
Georg Brandl 2008-06-23 12:04:56 +00:00
parent 1cafce9101
commit 8b0f48a38b
8 changed files with 80 additions and 8 deletions

View File

@ -13,6 +13,9 @@ New features added
* Sphinx now interprets field lists with fields like ``:param foo:`` * Sphinx now interprets field lists with fields like ``:param foo:``
in description units. in description units.
* The new `staticmethod` directive can be used to mark methods as
static methods.
* HTML output: * HTML output:
- The "previous" and "next" links have a more logical structure, so - The "previous" and "next" links have a more logical structure, so

View File

@ -199,6 +199,12 @@ The directives are:
parameter. The description should include similar information to that parameter. The description should include similar information to that
described for ``function``. See also :ref:`signatures`. described for ``function``. See also :ref:`signatures`.
.. directive:: .. staticmethod:: name(signature)
Like :dir:`method`, but indicates that the method is a static method.
.. versionadded:: 0.4
.. directive:: .. opcode:: name .. directive:: .. opcode:: name
Describes a Python bytecode instruction (this is not very useful for projects Describes a Python bytecode instruction (this is not very useful for projects

View File

@ -23,7 +23,7 @@ class desc(nodes.Admonition, nodes.Element): pass
class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass
# compatibility alias # compatibility alias
desc_classname = desc_addname desc_classname = desc_addname
# return type (C), object type (Python) # return type (C); object type, e.g. -> annotation (Python)
class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): pass class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): pass
# main name of object # main name of object
class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): pass class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): pass
@ -36,6 +36,8 @@ class desc_optional(nodes.Part, nodes.Inline, nodes.TextElement):
child_text_separator = ', ' child_text_separator = ', '
def astext(self): def astext(self):
return '[' + nodes.TextElement.astext(self) + ']' return '[' + nodes.TextElement.astext(self) + ']'
# annotation (not Python 3-style annotations)
class desc_annotation(nodes.Part, nodes.Inline, nodes.TextElement): pass
# node for content # node for content
class desc_content(nodes.General, nodes.Element): pass class desc_content(nodes.General, nodes.Element): pass

View File

@ -45,6 +45,18 @@ def desc_index_text(desctype, module, name):
return '%s() (%s.%s method)' % (methname, module, clsname) return '%s() (%s.%s method)' % (methname, module, clsname)
else: else:
return '%s() (%s method)' % (methname, clsname) return '%s() (%s method)' % (methname, clsname)
elif desctype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if module:
return '%s() (in module %s)' % (name, module)
else:
return '%s()' % name
if module:
return '%s() (%s.%s static method)' % (methname, module, clsname)
else:
return '%s() (%s static method)' % (methname, clsname)
elif desctype == 'attribute': elif desctype == 'attribute':
try: try:
clsname, attrname = name.rsplit('.', 1) clsname, attrname = name.rsplit('.', 1)
@ -91,6 +103,8 @@ doc_fields_with_arg = {
'var': 'Variable', 'var': 'Variable',
'ivar': 'Variable', 'ivar': 'Variable',
'cvar': 'Variable', 'cvar': 'Variable',
'returns': 'Returns',
'return': 'Returns',
} }
doc_fields_without_arg = { doc_fields_without_arg = {
@ -140,7 +154,7 @@ def handle_doc_fields(node):
nfield = nodes.field() nfield = nodes.field()
nfield += nodes.field_name(typ, typ) nfield += nodes.field_name(typ, typ)
nfield += nodes.field_body() nfield += nodes.field_body()
nfield[1] += children nfield[1] += fbody.children
new_list += nfield new_list += nfield
except (KeyError, ValueError): except (KeyError, ValueError):
fnametext = fname.astext() fnametext = fname.astext()
@ -199,6 +213,9 @@ def parse_py_signature(signode, sig, desctype, module, env):
add_module = True add_module = True
fullname = classname and classname + name or name fullname = classname and classname + name or name
if desctype == 'staticmethod':
signode += addnodes.desc_annotation('static ', 'static ')
if classname: if classname:
signode += addnodes.desc_addname(classname, classname) signode += addnodes.desc_addname(classname, classname)
# exceptions are a special case, since they are documented in the # exceptions are a special case, since they are documented in the
@ -210,7 +227,7 @@ def parse_py_signature(signode, sig, desctype, module, env):
signode += addnodes.desc_name(name, name) signode += addnodes.desc_name(name, name)
if not arglist: if not arglist:
if desctype in ('function', 'method'): if desctype in ('function', 'method', 'staticmethod'):
# for callables, add an empty parameter list # for callables, add an empty parameter list
signode += addnodes.desc_parameterlist() signode += addnodes.desc_parameterlist()
return fullname, classname return fullname, classname
@ -383,7 +400,7 @@ def desc_directive(desctype, arguments, options, content, lineno,
node.append(signode) node.append(signode)
try: try:
if desctype in ('function', 'data', 'class', 'exception', if desctype in ('function', 'data', 'class', 'exception',
'method', 'attribute'): 'method', 'staticmethod', 'attribute'):
name, clsname = parse_py_signature(signode, sig, desctype, module, env) name, clsname = parse_py_signature(signode, sig, desctype, module, env)
elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'): elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'):
name = parse_c_signature(signode, sig, desctype) name = parse_c_signature(signode, sig, desctype)
@ -457,7 +474,8 @@ def desc_directive(desctype, arguments, options, content, lineno,
if desctype in ('class', 'exception') and names: if desctype in ('class', 'exception') and names:
env.currclass = names[0] env.currclass = names[0]
clsname_set = True clsname_set = True
elif desctype in ('method', 'attribute') and clsname and not env.currclass: elif desctype in ('method', 'staticmethod', 'attribute') and \
clsname and not env.currclass:
env.currclass = clsname.strip('.') env.currclass = clsname.strip('.')
clsname_set = True clsname_set = True
# needed for association of version{added,changed} directives # needed for association of version{added,changed} directives
@ -482,6 +500,7 @@ desctypes = [
'data', 'data',
'class', 'class',
'method', 'method',
'staticmethod',
'attribute', 'attribute',
'exception', 'exception',
# the C ones # the C ones

View File

@ -254,7 +254,6 @@ def format_signature(what, obj):
def generate_rst(what, name, members, options, add_content, document, lineno, def generate_rst(what, name, members, options, add_content, document, lineno,
indent=u'', filename_set=None, check_module=False): indent=u'', filename_set=None, check_module=False):
env = document.settings.env env = document.settings.env
is_static = False
result = None result = None

View File

@ -113,6 +113,11 @@ class HTMLTranslator(BaseTranslator):
def depart_desc_optional(self, node): def depart_desc_optional(self, node):
self.body.append('<span class="optional">]</span>') self.body.append('<span class="optional">]</span>')
def visit_desc_annotation(self, node):
self.body.append(self.starttag(node, 'em', CLASS='property'))
def depart_desc_annotation(self, node):
self.body.append('</em>')
def visit_desc_content(self, node): def visit_desc_content(self, node):
self.body.append(self.starttag(node, 'dd', '')) self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node): def depart_desc_content(self, node):

View File

@ -86,7 +86,7 @@ class Desc(object):
def __init__(self, node): def __init__(self, node):
self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe') self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe')
self.ni = node['noindex'] self.ni = node['noindex']
self.type = self.cls = self.name = self.params = '' self.type = self.cls = self.name = self.params = self.annotation = ''
self.count = 0 self.count = 0
@ -284,6 +284,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'function' : 'funcdesc', 'function' : 'funcdesc',
'class': 'classdesc', 'class': 'classdesc',
'method': 'methoddesc', 'method': 'methoddesc',
'staticmethod': 'staticmethoddesc',
'exception': 'excdesc', 'exception': 'excdesc',
'data': 'datadesc', 'data': 'datadesc',
'attribute': 'memberdesc', 'attribute': 'memberdesc',
@ -325,7 +326,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
t2 = "{%s}{%s}" % (d.name, d.params) t2 = "{%s}{%s}" % (d.name, d.params)
elif d.env in ('datadesc', 'classdesc*', 'excdesc', 'csimplemacrodesc'): elif d.env in ('datadesc', 'classdesc*', 'excdesc', 'csimplemacrodesc'):
t2 = "{%s}" % (d.name) t2 = "{%s}" % (d.name)
elif d.env == 'methoddesc': elif d.env in ('methoddesc', 'staticmethoddesc'):
if d.cls: if d.cls:
t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params) t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params)
else: else:
@ -390,6 +391,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.descstack[-1].params = self.encode(node.astext().strip()) self.descstack[-1].params = self.encode(node.astext().strip())
raise nodes.SkipNode raise nodes.SkipNode
def visit_desc_annotation(self, node):
d = self.descstack[-1]
if d.env == 'describe':
d.name += self.encode(node.astext())
else:
self.descstack[-1].annotation = self.encode(node.astext().strip())
raise nodes.SkipNode
def visit_refcount(self, node): def visit_refcount(self, node):
self.body.append("\\emph{") self.body.append("\\emph{")
def depart_refcount(self, node): def depart_refcount(self, node):

View File

@ -572,6 +572,35 @@
\methodlineni{#2}{#3} \methodlineni{#2}{#3}
}{\end{fulllineitems}} }{\end{fulllineitems}}
% static method ----------------------------------------------------------
% \begin{staticmethoddesc}[classname]{methodname}{args}
\newcommand{\staticmethodline}[3][\@undefined]{
\staticmethodlineni{#2}{#3}
\ifx\@undefined#1\relax
\index{#2@{\py@idxcode{#2()}} (\py@thisclass\ static method)}
\else
\index{#2@{\py@idxcode{#2()}} (#1 static method)}
\fi
}
\newenvironment{staticmethoddesc}[3][\@undefined]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\staticmethodline{#2}{#3}
\else
\def\py@thisclass{#1}
\staticmethodline{#2}{#3}
\fi
}{\end{fulllineitems}}
% similar to {staticmethoddesc}, but doesn't add to the index
% (never actually uses the optional argument)
\newcommand{\staticmethodlineni}[3][\py@classbadkey]{%
\py@sigline{static \bfcode{#2}}{#3}}
\newenvironment{staticmethoddescni}[3][\py@classbadkey]{
\begin{fulllineitems}
\staticmethodlineni{#2}{#3}
}{\end{fulllineitems}}
% object data attribute -------------------------------------------------- % object data attribute --------------------------------------------------
% \begin{memberdesc}[classname]{membername} % \begin{memberdesc}[classname]{membername}
\newcommand{\memberline}[2][\py@classbadkey]{% \newcommand{\memberline}[2][\py@classbadkey]{%