diff --git a/CHANGES b/CHANGES index 520d88b3a..39c88a5ec 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ New features added * Sphinx now interprets field lists with fields like ``:param foo:`` in description units. +* The new `staticmethod` directive can be used to mark methods as + static methods. + * HTML output: - The "previous" and "next" links have a more logical structure, so diff --git a/doc/markup/desc.rst b/doc/markup/desc.rst index 73edfed4a..18d20afe7 100644 --- a/doc/markup/desc.rst +++ b/doc/markup/desc.rst @@ -199,6 +199,12 @@ The directives are: parameter. The description should include similar information to that 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 Describes a Python bytecode instruction (this is not very useful for projects diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 082265b2d..fe21981e6 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -23,7 +23,7 @@ class desc(nodes.Admonition, nodes.Element): pass class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass # compatibility alias 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 # main name of object 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 = ', ' def 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 class desc_content(nodes.General, nodes.Element): pass diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py index 4aa30f458..5094f8666 100644 --- a/sphinx/directives/desc.py +++ b/sphinx/directives/desc.py @@ -45,6 +45,18 @@ def desc_index_text(desctype, module, name): return '%s() (%s.%s method)' % (methname, module, clsname) else: 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': try: clsname, attrname = name.rsplit('.', 1) @@ -91,6 +103,8 @@ doc_fields_with_arg = { 'var': 'Variable', 'ivar': 'Variable', 'cvar': 'Variable', + 'returns': 'Returns', + 'return': 'Returns', } doc_fields_without_arg = { @@ -140,7 +154,7 @@ def handle_doc_fields(node): nfield = nodes.field() nfield += nodes.field_name(typ, typ) nfield += nodes.field_body() - nfield[1] += children + nfield[1] += fbody.children new_list += nfield except (KeyError, ValueError): fnametext = fname.astext() @@ -199,6 +213,9 @@ def parse_py_signature(signode, sig, desctype, module, env): add_module = True fullname = classname and classname + name or name + if desctype == 'staticmethod': + signode += addnodes.desc_annotation('static ', 'static ') + if classname: signode += addnodes.desc_addname(classname, classname) # 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) if not arglist: - if desctype in ('function', 'method'): + if desctype in ('function', 'method', 'staticmethod'): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() return fullname, classname @@ -383,7 +400,7 @@ def desc_directive(desctype, arguments, options, content, lineno, node.append(signode) try: if desctype in ('function', 'data', 'class', 'exception', - 'method', 'attribute'): + 'method', 'staticmethod', 'attribute'): name, clsname = parse_py_signature(signode, sig, desctype, module, env) elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'): 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: env.currclass = names[0] 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('.') clsname_set = True # needed for association of version{added,changed} directives @@ -482,6 +500,7 @@ desctypes = [ 'data', 'class', 'method', + 'staticmethod', 'attribute', 'exception', # the C ones diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 0490af7fc..98b3d96f0 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -254,7 +254,6 @@ def format_signature(what, obj): def generate_rst(what, name, members, options, add_content, document, lineno, indent=u'', filename_set=None, check_module=False): env = document.settings.env - is_static = False result = None diff --git a/sphinx/htmlwriter.py b/sphinx/htmlwriter.py index 7801e633f..fb1546a72 100644 --- a/sphinx/htmlwriter.py +++ b/sphinx/htmlwriter.py @@ -113,6 +113,11 @@ class HTMLTranslator(BaseTranslator): def depart_desc_optional(self, node): self.body.append(']') + def visit_desc_annotation(self, node): + self.body.append(self.starttag(node, 'em', CLASS='property')) + def depart_desc_annotation(self, node): + self.body.append('') + def visit_desc_content(self, node): self.body.append(self.starttag(node, 'dd', '')) def depart_desc_content(self, node): diff --git a/sphinx/latexwriter.py b/sphinx/latexwriter.py index 0bcb501ff..9c2428766 100644 --- a/sphinx/latexwriter.py +++ b/sphinx/latexwriter.py @@ -86,7 +86,7 @@ class Desc(object): def __init__(self, node): self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe') 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 @@ -284,6 +284,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'function' : 'funcdesc', 'class': 'classdesc', 'method': 'methoddesc', + 'staticmethod': 'staticmethoddesc', 'exception': 'excdesc', 'data': 'datadesc', 'attribute': 'memberdesc', @@ -325,7 +326,7 @@ class LaTeXTranslator(nodes.NodeVisitor): t2 = "{%s}{%s}" % (d.name, d.params) elif d.env in ('datadesc', 'classdesc*', 'excdesc', 'csimplemacrodesc'): t2 = "{%s}" % (d.name) - elif d.env == 'methoddesc': + elif d.env in ('methoddesc', 'staticmethoddesc'): if d.cls: t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params) else: @@ -390,6 +391,14 @@ class LaTeXTranslator(nodes.NodeVisitor): self.descstack[-1].params = self.encode(node.astext().strip()) 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): self.body.append("\\emph{") def depart_refcount(self, node): diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 86ee5c851..38cba2eca 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -572,6 +572,35 @@ \methodlineni{#2}{#3} }{\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 -------------------------------------------------- % \begin{memberdesc}[classname]{membername} \newcommand{\memberline}[2][\py@classbadkey]{%