How to make Sphinx understand Sage doctests? - python

I have a package that is primarily Python, and mostly meant to be used with Python. But also there are a few extra functions that are available when the module is used under Sage. The problem is that Sage doctests must be prefixed by sage: rather than >>>, and Sphinx doesn't pick these up when generating the documentation.
Is there a way to get Sphinx to recognize the sage: prefix as being equivalent to >>> when generating the HTML (or other) docs?

Well, you can use Sage's built-in version of Sphinx and its documentation builder. Work in progress for Sage at http://trac.sagemath.org/ticket/13679 allows for building the documentation for a single Python file which is not in Sage's source tree, so you could try that.

I finally found out how to preprocess the docstring, to change sage: to >>>. The following goes into my project's doc/conf.py:
# Adapted from http://stackoverflow.com/a/11746519/1048959
def process_docstring(app, what, name, obj, options, lines):
for i in range(len(lines)):
lines[i] = re.sub(r'^(\s*)sage: ', r'\1>>> ', lines[i])
def setup(app):
app.connect('autodoc-process-docstring', process_docstring)
At least now Sphinx can parse my docstrings without generating errors. I'm still leaving this question open though because there is still a problem: the generated documentation shows >>> rather than sage:, which can be misleading to the reader.

Okay, here's another idea: try prefacing your indented blocks with double colons. For example, in slices.rst, change
You can use numpy style indexes:
>>> x[0, 0]
0j
to
You can use numpy style indexes::
sage: x[0, 0]
0j
(I added the double colon, and I changed the prompt to sage:.) I tried this with your code, but commenting out your modification to conf.py. See the Sphinx docs for source code blocks.
Then you need to modify one Sphinx file:
diff -ur sphinx/highlighting.py sphinx/highlighting.py
--- sphinx/highlighting.py 2010-08-11 17:17:48.000000000 +0200
+++ sphinx/highlighting.py 2010-11-28 12:04:44.068642703 +0100
## -161,7 +161,7 ##
# find out which lexer to use
if lang in ('py', 'python'):
- if source.startswith('>>>'):
+ if source.startswith('>>>') or source.startswith('sage: '):
# interactive session
lexer = lexers['pycon']
else:

Related

Python markdown edge case: /* */

Is there a way to get Python to interpret Markdown the same way as it is interpreted here on stackoverflow:
This is a C comment: /* */ tada!
and on github? https://gist.github.com/jason-s/fc81280dc6108f9ec3a8
Python's markdown module interprets the * * as italics:
>>> import markdown
>>> markdown.markdown('This is a C comment: /* */ tada!')
u'<p>This is a C comment: /<em> </em>/ tada!</p>'
(Babelmark 2 shows some of the differences. Looks like there are different interpretations of the markdown syntax.)
The /* */ syntax is not standard Markdown. In fact, it is not mentioned at all in the syntax rules. Therefore, it is less likely to be handled consistently among different Markdown implementations.
If it is a C comment, then it is "code" and should probably be marked up as such. Either in a code block or using inline code backticks (`/* */`). As mentioned in a comment to the OP, it could also be escaped with backslashes if you really don't want it marked up as code. Personally, I would instruct the author to fix their documents (regardless of parser behavior).
In fact, the Markdown parsers that do ignore it do so by accident. In an effort to avoid matching a few edge cases that should not be interpreted as emphasis, they require a word boundary before the opening asterisk (but not after it) and a word boundary after the closing asterisk (but not before it) to consider is as emphasis. Because the C comment has a slash before the opening asterisk (and a space after it) and a slash after the closing asterisk (and a space before it), some parsers do not see it as emphasis. I suspect you will find that those same parsers fail to identify a few edge cases as emphasis that should be. And as the Syntax Rules are silent on these edge cases, each implementation gets them slightly different. I would even go so far as to say that the implementations that do not see that as emphasis are potentially in the wrong here. But this is not the place to debate that.
That said, you are using Python-Markdown, which has a comprehensive Extension API. If an existing third party extension does not already exist (see below), you can create your own. You may add your own pattern to match the C comment specifically and handle it however you like. Or you may override the parser's default handling of emphasis and make it match some other implementation who's behavior you desire.
Actually, the BetterEm Extension (which, for some reason is not on the list of third party extensions) might do the later and give you the behavior you want. Unfortunately, it does not ship by itself, but as part of a larger package which includes multiple extensions. Of course, you only need to use the one. To get it working you need to install it. Unfortunately, it does not appear to be hosted on PyPI, so you'll have to download it directly from GitHub. The following command should download and install it all in one go:
pip install https://github.com/facelessuser/pymdown-extensions/archive/master.zip
Once you have successfully installed it just do the following in Python:
>>> import markdown
>>> html = markdown.markdown(yourtext, extensions=['pymdownx.betterem'])
Or from the commandline:
python -m markdown -x 'pymdownx.betterem' yourinputfile.md > output.html
Note: I have not tested the BetterEm Extension. It may or may not give you the behavior you want. According to the docs, "the behavior will be very similar in feel to GFM bold and italic (but not necessarily exact)."
well, it's ugly, but this post-Markdown-processing patch seems to do what I want.
>>> import markdown
>>> mdtext = 'This is a C comment: /* */ tada!'
>>> mdhtml = markdown.markdown(mdtext)
>>> mdhtml
u'<p>This is a C comment: /<em> </em>/ tada!</p>'
>>> import re
>>> mdccommentre = re.compile('/<em>( | .* )</em>/')
>>> mdccommentre.sub('/*\\1*/',mdhtml)
u'<p>This is a C comment: /* */ tada!</p>'

Making Sphinx produce untypogrified code blocks in PDF output

Code blocks look ugly (check the quotes) in PDF output:
I use version 1.1.3 of Sphinx, and used the following command to produce the doc:
$ make latexpdf
Also, copying the snippet from the PDF destroys the indenting when pasting:
#view_config(route_name=’hello’)
def hello_world(request):
return Response(’Hello World!’)
I would expect this:
#view_config(route_name=’hello’)
def hello_world(request):
return Response(’Hello World!’)
This would be even nicer:
#view_config(route_name='hello')
def hello_world(request):
return Response('Hello World!')
Sphinx is really an excellent tool but I also have a few issues with the default PDF output of the latexpdf target.
Specifically:
Single quotes in code blocks are converted to acute style quotes which doesn't look right in source code.
The code blocks aren't indented from the main text. For me this makes them less readable.
I prefer other fonts and pygments but that is just a personal choice and can be configured.
Some of this can be fixed in the LaTeX pre-amble section of the Sphinx conf.py but the quotes are modified by Sphinx to custom LaTeX entities so the upquote LaTeX package can't be used to correct them.
After a good bit of experimentation with different config options I ended up writing a small script to modify the LaTeX source prior to building the PDF. The script is here and the output that I wanted to generate is here. (For comparison here is the default output for the same document.)
If someone has a cleaner solution, for example one that could be done completely through Sphinx conf.py so the changes would be picked up by ReadTheDocs then I would be interested.
Also, the issue with losing indentation when copying and pasting from the PDF probably isn't a Sphinx/LaTeX issue.
This is only a partial answer which may lead toward an ultimate solution. To disable typographer quotes (also known as curly or smart quotes) for HTML output in Sphinx, change the default setting in conf.py for SmartyPants from True to False.
I assume that one could find the function in Sphinx that transforms quotes and use the same logic from the HTML output and apply it for PDF output.
The option proposed by Steve Piercy now no longer works (since Sphinx 1.6):
Deprecated since version 1.6: Use the smart_quotes option
in the Docutils configuration file (docutils.conf) instead.
What was not obvious to me was how to apply this setting. You need to create a docutils.conf file and put it in one of these locations:
/etc/docutils.conf
./docutils.conf
~/.docutils
The easiest option is to put it where you are building the docs from - YMMV. It should contain at least this:
[general]
smart_quotes: no

How do you enable block folding for Python comments in TextMate?

In TextMate 1.5.10 r1623, you get little arrows that allow you to fold method blocks:
Unfortunately, if you have a multi-lined Python comment, it doesn't recognize it, so you can't fold it:
def foo():
"""
How do
I fold
these comments?
"""
print "bar"
TextMate has this on their site on how to customize folding: http://manual.macromates.com/en/navigation_overview#customizing_foldings
...but I'm not skilled in regex enough to do anything about it. TextMate uses the Oniguruma regex API, and I'm using the default Python.tmbundle updated to the newest version via GetBundles.
Does anyone have an idea of how to do this? Thanks in advance for your help! :)
Adding the default foldingStartMarker and foldingStopMarker regex values for Python.tmbundle under the Python Language in Bundle Editor:
foldingStartMarker = '^\s*(def|class)\s+([.a-zA-Z0-9_ <]+)\s*(\((.*)\))?\s*:|\{\s*$|\(\s*$|\[\s*$|^\s*"""(?=.)(?!.*""")';
foldingStopMarker = '^\s*$|^\s*\}|^\s*\]|^\s*\)|^\s*"""\s*$';
It appears that multi-line comment folding does work in TextMate, but your must line up your quotes exactly like so:
""" Some sort of multi
line comment, which needs quotes
in just the right places to work. """
That seems to do it:
According to this Textmate Mailing list thread, if you follow it to the end, proper code folding for Python is not supported. Basically, regular expressions as implemented in the foldingStartMarker and foldingStopMarker do not allow for captures, thus the amount of spacing at the beginning of the "end fold" cannot be matched to the "begin fold".
The issue is not finally and officially addressed by Textmate's creator, Allan Odgaard; however since the thread is from 2005, I assume it is a dead issue, and not one that will be supported.

Is it possible to access the source code of a python script passed to python on standard in?

This is a bit of a random question that is more out of curiosity than any specific need.
Is it possible to write some python code that will print some stuff out, including the source code itself, without having the python code stored in a file? For example, doing something like this at the Bash prompt:
$ echo '
> print "The Code:"
> PrintScript() # What would this function look like?
> for i in range(5):
> print i,
> print "!"
> ' | python
and get an output like this:
The Code:
print "The Code:"
PrintScript() # What would this function look like?
for i in range(5):
print i,
print "!"
0 1 2 3 4 5 !
I suspect that this probably can't be done, but given python's introspection capabilities, I was curious to know whether it extended to this level.
That's the closest I'm getting:
echo 'import __main__,inspect;print inspect.getsource(__main__)' | python
which fails... In any case, the original code is eaten up (read from stdin) by the interpreter at startup. At most you may be able to get to the compiled code, again through the __main__ module.
Update:
The dis module is supposed to give you a disassembly of all functions in a module, but even that one isn't seeing any code:
$ echo -e 'import __main__,dis;print dis.dis(__main__)' | python
None
And even when I throw in a function:
$ echo -e "import __main__,dis;print dis.dis(__main__)\ndef x():\n pass" | python
None
Yes, it is indeed possible to write a program which outputs it's own source. You don't need even introspection for this tasks, you just need to be able to print computed strings (works with every language).
The technique is called Quine and here is a rather short example in Python:
quine = 'quine = %r\r\nprint quine %% quine'
print quine % quine
But quines aren't limited to such simple programs. They can do much more, for example printing their own source backwards and so on... :)
print open(__file__).read(),
This will work on UNIX systems I think, but I'm not sure about Windows. The trailing comma makes sure that the source code is printed exactly, without an extra trailing newline.
Just realized (based on the comments below) that this does not work if your source code comes from sys.stdin, which is exactly what you were asking for. In that case, you might take advantage of some of the ideas outlined on this page about quines (programs printing their own source codes) in Python, but none of the solutions would be a single function that just works. A language-independent discussion is here.
So, in short, no, I don't think this is possible with a single function if your source code comes from the standard input. There might be a possibility to access the interpreted form of your program as a Python code object and translate that back into source form, but the translated form will almost surely not match the original file contents exactly. (For instance, the comments and the shebang line would definitely be stripped away).
closest you can get is using readline to interrogate the command history if available from what i can see e.g. but i suspect this may not contain stuff piped into the session and would only work for interactive sessions anyway

How to fold long docstrings in python source code in VIM?

Does anybody know of a method, or perhaps a plugin, that will
automatically fold long docstrings in Python? I have docstrings in my
code that span several pages, so it is troublesome to keep paging
through them. The other tricky part is that there is embedded python
testing code in the docstrings, so that might make parsing them
difficult. Note that I only need to automatically fold the entire
docstring, regardless of what is in it.
This is a bit of a dirty hack, but you can go through the python syntax file (:sp $VIMRUNTIME/syntax/python.vim) and find all the syntax regions for triple-quoted strings (search for ''' and """) and add the fold keyword to the end of those statements. Then just set foldmethod=syntax for python files and the comments should be folded.
I'm not sure about a plugin or automation, but if you type zf/ you can then search for something and it will fold up to the next instance of it. So in a document like the following (where [] is the cursor):
def foo():
"""[]
Some long docstring
that takes up many
lines
"""
pass
Look at edit2 first for the updated search string!
If you use the command zf/"""[ENTER], it should fold everything from the current line (the beginning of the docstring) to the next occurrence of """ which should be the end of the docstring.
I know this isn't automation, but perhaps it will help in the interim, or lead you down the right path to automating it. See edit2 for a better search function, although I still don't know how to automate.
Hope this helps.
Edit: in a corollary, you can search for any docstring with /"""\_.\{-}""", although this will also return the code within the docstring. To search for a function definition followed by a docstring, you can use /def\_.\{-}"""\_.\{-}""", although this breaks on a def inside the docstring.
Edit2: Actually, some more playing with regexs led me to this: /def.\{-}):\_s*"""\_.\{-}""" which should find any function followed by a docstring. It searches for def followed by any characters, then ): followed by a newline and/or whitespace followed by """ followed by any number of lines than the next """, but always ensures the 2nd triple quote is the one immediately following the first.
In your .vimrc add:
" folding
set foldmethod=indent
This will auto-fold at every indentation, which, in python, translates to docstrings. It works VERY VERY nice. Give it a try. The above answer is correct, but requires a bunch of keystrokes (blah!)
I think I've found a slight improvement to the answer of #too_much_php by trying to figure out how to do the same thing without root access. Copying the syntax region definitions from $VIMRUNTIME/syntax/python.vim to ~/.vim/after/syntax/python.vim and editing them solved the problem for me. Here's my code for ~/.vim/after/syntax/python.vim:
syn region docString1
\ start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend transparent fold
syn region docString2
\ start=+[uU]\=[rR]\z('''\|"""\)+ end="\z1" keepend transparent fold
Now I can run :set foldmethod=syntax to fold docstrings.
PS: Credits to #too_much_php for the initial idea
You can do this with :set foldmethod=marker foldmarker=""",""", I think. I haven't tested it, but that should do the trick. The arguments to foldmarker are start and end markers.
I wrote a vimscript plugin many years ago to do exactly this, but never got around to publishing it. I've put it up as a gist, but have also pasted the source below. I've also replicated its functionality for Sublime Text here.
"
" Fold multi-line Python comments into one line.
"
" Also maps the "-" key to toggle expansion and <C-f> to toggle all folding.
"
setlocal foldmethod=syntax
setlocal foldtext=FoldText()
setlocal fillchars=
map <buffer> - za
map <buffer> <C-f> :call ToggleFold()<CR>
let b:folded = 1
hi Folded gui=bold cterm=bold guifg=cyan ctermfg=cyan guibg=NONE ctermbg=NONE
function! ToggleFold()
if b:folded == 0
exec "normal! zM"
let b:folded = 1
else
exec "normal! zR"
let b:folded = 0
endif
endfunction
function! s:Strip(string)
return substitute(a:string, '^[[:space:][:return:][:cntrl:]]\+\|[[:space:][:return:][:cntrl:]]\+$', '', '')
endfunction
" Extract the first line of a multi-line comment to use as the fold snippet
function! FoldText()
let l:snippet = getline(v:foldstart)
if len(s:Strip(l:snippet)) == 3
let l:snippet = strpart(l:snippet, 1) . s:Strip(getline(v:foldstart + 1))
endif
return '+' . l:snippet . ' ...'
endfunction
My solution involves using SimpylFold. After installing it using Vundle, I put this in my vimrc file:
autocmd FileType python setlocal foldlevel=2
Which makes all docstrings folded by default when you open a python file, which is super awesome.

Categories