Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Cython: embed signatures in docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
jdemeyer committed Apr 7, 2015
1 parent 819000f commit 1a36cc9
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build/pkgs/cython/package-version.txt
@@ -1 +1 @@
0.22
0.22.p1
44 changes: 44 additions & 0 deletions build/pkgs/cython/patches/embedsignatures.patch
@@ -0,0 +1,44 @@
commit 9139a7f836151fb5bdb1624a05dce13b1bb17164
Author: Stefan Behnel <stefan_ml@behnel.de>
Date: Mon Apr 6 10:45:48 2015 +0200

support NULL as default argument in auto doc transform

diff --git a/Cython/Compiler/AutoDocTransforms.py b/Cython/Compiler/AutoDocTransforms.py
index 775f635..88b0cd8 100644
--- a/Cython/Compiler/AutoDocTransforms.py
+++ b/Cython/Compiler/AutoDocTransforms.py
@@ -51,6 +51,8 @@ class EmbedSignature(CythonTransform):
default_val = arg.default
if not default_val:
return None
+ if isinstance(default_val, ExprNodes.NullNode):
+ return 'NULL'
try:
denv = self.denv # XXX
ctval = default_val.compile_time_value(self.denv)
diff --git a/tests/run/embedsignatures.pyx b/tests/run/embedsignatures.pyx
index 0bfebfe..781cd21 100644
--- a/tests/run/embedsignatures.pyx
+++ b/tests/run/embedsignatures.pyx
@@ -199,6 +199,9 @@ __doc__ = ur"""

>>> print(funcdoc(f_defexpr5))
f_defexpr5(int x=4)
+
+ >>> print(funcdoc(f_charptr_null))
+ f_charptr_null(char *s=NULL) -> char *
"""

cdef class Ext:
@@ -403,6 +406,10 @@ cpdef f_defexpr4(int x = (Ext.CONST1 + FLAG1) * Ext.CONST2):
cpdef f_defexpr5(int x = 2+2):
pass

+cpdef (char*) f_charptr_null(char* s=NULL):
+ return s or b'abc'
+
+
# no signatures for lambda functions
lambda_foo = lambda x: 10
lambda_bar = lambda x: 20
2 changes: 1 addition & 1 deletion src/sage/misc/lazy_attribute.pyx
Expand Up @@ -66,7 +66,7 @@ cdef class _lazy_attribute(object):
sage: Parent.element_class
<sage.misc.lazy_attribute.lazy_attribute object at 0x...>
sage: Parent.element_class.__doc__[64:120]
sage: Parent.element_class.__doc__[91:147]
'The (default) class for the elements of this parent\n\n '
sage: Parent.element_class.__name__
'element_class'
Expand Down
24 changes: 14 additions & 10 deletions src/sage/misc/sagedoc.py
Expand Up @@ -428,10 +428,10 @@ def format(s, embedded=False):
EXAMPLES::
sage: from sage.misc.sagedoc import format
sage: identity_matrix(2).rook_vector.__doc__[110:182]
sage: identity_matrix(2).rook_vector.__doc__[201:273]
'Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard'
sage: format(identity_matrix(2).rook_vector.__doc__[110:182])
sage: format(identity_matrix(2).rook_vector.__doc__[201:273])
'Let A be an m by n (0,1)-matrix. We identify A with a chessboard\n'
If the first line of the string is 'nodetex', remove 'nodetex' but
Expand Down Expand Up @@ -516,15 +516,19 @@ def format(s, embedded=False):
# be subject to formatting (line breaks must not be inserted).
# Hence, we first try to find out whether there is an embedding
# information.
first_newline = s.find(os.linesep)
from sage.misc.sageinspect import _extract_embedded_position

# Check for embedding info in first two lines
L = s.splitlines()
embedding_info = ''
if first_newline > -1:
first_line = s[:first_newline]
from sage.misc.sageinspect import _extract_embedded_position
if _extract_embedded_position(first_line) is not None:
embedding_info = first_line + os.linesep
s = s[first_newline+len(os.linesep):]
# Hence, by now, s starts with the second line.
if len(L) >= 2 and _extract_embedded_position(L[0]) is not None:
# Embedding info is on first line
embedding_info = L[0] + os.linesep
s = os.linesep.join(L[1:])
if len(L) >= 3 and _extract_embedded_position(L[1]) is not None:
# Embedding info is on second line
embedding_info = L[1] + os.linesep
s = os.linesep.join(L[2:])
else:
from sage.misc.sageinspect import _extract_embedded_position
if _extract_embedded_position(s) is not None:
Expand Down
69 changes: 46 additions & 23 deletions src/sage/misc/sageinspect.py
Expand Up @@ -162,7 +162,7 @@ def isclassinstance(obj):
# Parse strings of form "File: sage/rings/rational.pyx (starting at line 1080)"
# "\ " protects a space in re.VERBOSE mode.
__embedded_position_re = re.compile(r'''
\A # anchor to the beginning of the string
^ # anchor to the beginning of the line
File:\ (?P<FILENAME>.*?) # match File: then filename
\ \(starting\ at\ line\ (?P<LINENO>\d+)\) # match line number
\n? # if there is a newline, eat it
Expand Down Expand Up @@ -204,19 +204,22 @@ def _extract_embedded_position(docstring):
- Extensions by Nick Alexander
- Extension for interactive Cython code by Simon King
"""
if docstring is None:
try:
res = __embedded_position_re.search(docstring)
except TypeError:
return None

if res is None:
return None
res = __embedded_position_re.match(docstring)
if res is not None:
raw_filename = res.group('FILENAME')
filename = os.path.join(SAGE_SRC, raw_filename)
if not os.path.isfile(filename):
from sage.misc.misc import SPYX_TMP
filename = os.path.join(SPYX_TMP, '_'.join(raw_filename.split('_')[:-1]), raw_filename)
lineno = int(res.group('LINENO'))
original = res.group('ORIGINAL')
return (original, filename, lineno)
return None

raw_filename = res.group('FILENAME')
filename = os.path.join(SAGE_SRC, raw_filename)
if not os.path.isfile(filename):
from sage.misc.misc import SPYX_TMP
filename = os.path.join(SPYX_TMP, '_'.join(raw_filename.split('_')[:-1]), raw_filename)
lineno = int(res.group('LINENO'))
original = res.group('ORIGINAL')
return (original, filename, lineno)


class BlockFinder:
Expand Down Expand Up @@ -1490,31 +1493,51 @@ def _sage_getdoc_unformatted(obj):
TESTS:
Test that we suppress useless built-in output (Ticket #3342)
Test that we suppress useless built-in output (:trac:`3342`)::
sage: from sage.misc.sageinspect import _sage_getdoc_unformatted
sage: _sage_getdoc_unformatted(isinstance.__class__)
''
If ``__init__`` has embedding information, we prefer that::
sage: C = QQ['x', 'y'].__class__
sage: print C.__doc__
MPolynomialRing_libsingular(base_ring, n, names, order='degrevlex')
sage: print C.__init__.__doc__
File: sage/rings/polynomial/multi_polynomial_libsingular.pyx (starting at line ...)
<BLANKLINE>
Construct a multivariate polynomial ring...
sage: print _sage_getdoc_unformatted(C)
File: sage/rings/polynomial/multi_polynomial_libsingular.pyx (starting at line ...)
<BLANKLINE>
Construct a multivariate polynomial ring...
AUTHORS:
- William Stein
- extensions by Nick Alexander
"""
if obj is None: return ''
r = None
if obj is None:
return ''
try:
r = obj._sage_doc_()
except (AttributeError, TypeError): # the TypeError occurs if obj is a class
r = obj.__doc__

#Check to see if there is an __init__ method, and if there
#is, use its docstring.
if r is None and hasattr(obj, '__init__'):
r = obj.__init__.__doc__

if r is None:
return ''
# If r contains no embedding information, try __init__
if _extract_embedded_position(r) is None:
try:
initdoc = obj.__init__.__doc__
except AttributeError:
pass
else:
# Prefer initdoc if it does have embedding information
# or if r was None
if r is None or _extract_embedded_position(initdoc) is not None:
r = initdoc
if r is None:
return ''

# Check if the __doc__ attribute was actually a string, and
# not a 'getset_descriptor' or similar.
Expand Down
5 changes: 0 additions & 5 deletions src/sage/structure/dynamic_class.py
Expand Up @@ -393,11 +393,6 @@ def _sage_src_lines():
methods['_sage_src_lines_'] = _sage_src_lines
methods['__doc__'] = doccls.__doc__
methods['__module__'] = doccls.__module__
#if "_sage_doc_" not in methods:
# from sage.misc.sageinspect import sage_getdoc
# def _sage_getdoc(obj):
# return sage_getdoc(cls)
# methods['_sage_src_lines_'] = _sage_getdoc

metaclass = DynamicMetaclass
# The metaclass of a class must derive from the metaclasses of its
Expand Down
2 changes: 2 additions & 0 deletions src/setup.py
Expand Up @@ -546,6 +546,7 @@ def run_cythonize():
version_file = os.path.join(os.path.dirname(__file__), '.cython_version')
version_stamp = '\n'.join([
'cython version: ' + str(Cython.__version__),
'embedsignature: True'
'debug: ' + str(debug),
'profile: ' + str(profile),
])
Expand All @@ -559,6 +560,7 @@ def run_cythonize():
build_dir='build/cythonized',
force=force,
compiler_directives={
'embedsignature': True,
'profile': profile,
})

Expand Down

0 comments on commit 1a36cc9

Please sign in to comment.