Close #23: Added a `classmethod directive along with method`

and ``staticmethod``.
This commit is contained in:
Georg Brandl 2008-12-29 00:11:06 +01:00
parent 1aa8baa0d8
commit f13c3a00cd
6 changed files with 83 additions and 20 deletions

View File

@ -40,6 +40,9 @@ New features added
- #6: Don't generate redundant ``<ul>`` for top-level TOC tree - #6: Don't generate redundant ``<ul>`` for top-level TOC tree
items, which leads to a visual separation of TOC entries. items, which leads to a visual separation of TOC entries.
- #23: Added a ``classmethod`` directive along with ``method``
and ``staticmethod``.
* Configuration: * Configuration:
- The new ``html_add_permalinks`` config value can be used to - The new ``html_add_permalinks`` config value can be used to

View File

@ -205,6 +205,12 @@ The directives are:
.. versionadded:: 0.4 .. versionadded:: 0.4
.. directive:: .. classmethod:: name(signature)
Like :dir:`method`, but indicates that the method is a class method.
.. versionadded:: 0.6
.. _signatures: .. _signatures:

View File

@ -58,6 +58,18 @@ def desc_index_text(desctype, module, name, add_modules):
return _('%s() (%s.%s static method)') % (methname, module, clsname) return _('%s() (%s.%s static method)') % (methname, module, clsname)
else: else:
return _('%s() (%s static method)') % (methname, clsname) return _('%s() (%s static method)') % (methname, clsname)
elif desctype == 'classmethod':
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 class method)' % (methname, module, clsname)
else:
return '%s() (%s class method)' % (methname, clsname)
elif desctype == 'attribute': elif desctype == 'attribute':
try: try:
clsname, attrname = name.rsplit('.', 1) clsname, attrname = name.rsplit('.', 1)
@ -258,6 +270,8 @@ def parse_py_signature(signode, sig, desctype, module, env):
if desctype == 'staticmethod': if desctype == 'staticmethod':
signode += addnodes.desc_annotation('static ', 'static ') signode += addnodes.desc_annotation('static ', 'static ')
elif desctype == 'classmethod':
signode += addnodes.desc_annotation('classmethod ', 'classmethod ')
if classname: if classname:
signode += addnodes.desc_addname(classname, classname) signode += addnodes.desc_addname(classname, classname)
@ -270,7 +284,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', 'staticmethod'): if desctype in ('function', 'method', 'staticmethod', 'classmethod'):
# for callables, add an empty parameter list # for callables, add an empty parameter list
signode += addnodes.desc_parameterlist() signode += addnodes.desc_parameterlist()
if retann: if retann:
@ -433,7 +447,8 @@ 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', 'staticmethod', 'attribute'): 'method', 'staticmethod', 'classmethod',
'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)
@ -510,8 +525,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', 'staticmethod', 'attribute') and \ elif desctype in ('method', 'staticmethod', 'classmethod',
clsname and not env.currclass: '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
@ -536,6 +551,7 @@ desctypes = [
'data', 'data',
'class', 'class',
'method', 'method',
'classmethod',
'staticmethod', 'staticmethod',
'attribute', 'attribute',
'exception', 'exception',

View File

@ -46,15 +46,18 @@ class Options(object):
pass pass
def is_static_method(obj): def get_method_type(obj):
"""Check if the object given is a static method.""" """
if isinstance(obj, (FunctionType, classmethod)): Return the method type for an object: method, staticmethod or classmethod.
return True """
elif isinstance(obj, BuiltinFunctionType): if isinstance(obj, classmethod) or \
return obj.__self__ is not None (isinstance(obj, MethodType) and obj.im_self is not None):
elif isinstance(obj, MethodType): return 'classmethod'
return obj.im_self is not None elif isinstance(obj, FunctionType) or \
return False (isinstance(obj, BuiltinFunctionType) and obj.__self__ is not None):
return 'staticmethod'
else:
return 'method'
class AutodocReporter(object): class AutodocReporter(object):
@ -353,7 +356,8 @@ class RstGenerator(object):
""" """
Return the signature of the object, formatted for display. Return the signature of the object, formatted for display.
""" """
if what not in ('class', 'method', 'function'): if what not in ('class', 'method', 'staticmethod', 'classmethod',
'function'):
return '' return ''
err = None err = None
@ -378,7 +382,8 @@ class RstGenerator(object):
getargs = False getargs = False
if getargs: if getargs:
argspec = inspect.getargspec(obj) argspec = inspect.getargspec(obj)
if what in ('class', 'method') and argspec[0] and \ if what in ('class', 'method', 'staticmethod',
'classmethod') and argspec[0] and \
argspec[0][0] in ('cls', 'self'): argspec[0][0] in ('cls', 'self'):
del argspec[0][0] del argspec[0][0]
args = inspect.formatargspec(*argspec) args = inspect.formatargspec(*argspec)
@ -455,8 +460,10 @@ class RstGenerator(object):
self.result.append(u'', '') self.result.append(u'', '')
# now, create the directive header # now, create the directive header
directive = (what == 'method' and is_static_method(todoc)) \ if what == 'method':
and 'staticmethod' or what directive = get_method_type(todoc)
else:
directive = what
self.result.append(indent + u'.. %s:: %s%s' % self.result.append(indent + u'.. %s:: %s%s' %
(directive, name_in_directive, sig), '<autodoc>') (directive, name_in_directive, sig), '<autodoc>')
if what == 'module': if what == 'module':
@ -507,7 +514,8 @@ class RstGenerator(object):
self.result.append(indent + line, src[0], src[1]) self.result.append(indent + line, src[0], src[1])
# document members? # document members?
if not members or what in ('function', 'method', 'data', 'attribute'): if not members or what in ('function', 'method', 'staticmethod',
'classmethod', 'data', 'attribute'):
return return
# set current namespace for finding members # set current namespace for finding members
@ -591,7 +599,7 @@ class RstGenerator(object):
source='') source='')
else: else:
memberwhat = 'class' memberwhat = 'class'
elif callable(member): elif inspect.isroutine(member):
memberwhat = 'method' memberwhat = 'method'
elif isdescriptor(member): elif isdescriptor(member):
memberwhat = 'attribute' memberwhat = 'attribute'

View File

@ -451,6 +451,35 @@
\fi \fi
}{\end{fulllineitems}} }{\end{fulllineitems}}
% class method ----------------------------------------------------------
% \begin{classmethoddesc}[classname]{methodname}{args}
\newcommand{\classmethodline}[3][\@undefined]{
\classmethodlineni{#2}{#3}
\ifx\@undefined#1\relax
\index{#2@{\py@idxcode{#2()}} (\py@thisclass\ class method)}
\else
\index{#2@{\py@idxcode{#2()}} (#1 class method)}
\fi
}
\newenvironment{classmethoddesc}[3][\@undefined]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\classmethodline{#2}{#3}
\else
\def\py@thisclass{#1}
\classmethodline{#2}{#3}
\fi
}{\end{fulllineitems}}
% similar to {classmethoddesc}, but doesn't add to the index
% (never actually uses the optional argument)
\newcommand{\classmethodlineni}[3][\py@classbadkey]{%
\py@sigline{class \bfcode{#2}}{#3}}
\newenvironment{classmethoddescni}[3][\py@classbadkey]{
\begin{fulllineitems}
\classmethodlineni{#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]{%

View File

@ -399,6 +399,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'function' : 'funcdesc', 'function' : 'funcdesc',
'class': 'classdesc', 'class': 'classdesc',
'method': 'methoddesc', 'method': 'methoddesc',
'classmethod': 'classmethoddesc',
'staticmethod': 'staticmethoddesc', 'staticmethod': 'staticmethoddesc',
'exception': 'excdesc', 'exception': 'excdesc',
'data': 'datadesc', 'data': 'datadesc',
@ -441,7 +442,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
t2 = "{%s}{%s}" % (d.name, d.params) t2 = "{%s}{%s}" % (d.name, d.params)
elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'): elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'):
t2 = "{%s}" % (d.name) t2 = "{%s}" % (d.name)
elif d.env in ('methoddesc', 'staticmethoddesc'): elif d.env in ('methoddesc', 'classmethoddesc', '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: