Sphinx autodoc - automated API documentation (PyCon APAC 2015 in Taiwan)

Preview:

Citation preview

Sphinx autodocautomated API documentation

Takayuki Shimizukawa

Sphinx co-maintainer

Sphinx-users.jp

1

Who am I

@shimizukawa

1. Sphinx co-maintainer

2. Sphinx-users.jp

3. PyCon JP committee

BePROUD co, ltd.2

http://pycon.jp/

3

Early Bird Registration: 210 tickets.

Speaker Call for Proposals: until July 15th.

4

180 tickets gone

Sphinx autodoc

5

Do you know the docstring?

6

1. def dumps(obj, ensure_ascii=True):2.      """Serialize ``obj`` to a JSON formatted

``str``.3. """4. 5. ...6. return ...

Line 2,3 is a docstring

You can see the string by "help(dumps)"

Docstring

7

Have you writtenAPI docs

using docstrings?

8

What is the reason you do not write docstrings.

I don't know what/where should I write.

Are there some docstring format spec?

It's not beneficial.

I'll tell you a good way to write the docstrings.

9

Goal of this session

How to generate a doc from Python source code.

re-discovering the meaning of docstrings.

10

Sphinx introduction& How to setup it

11

What is Sphinx?

Sphinx is a Open Source documentation generator.

Sphinx was created to generate Python official library's document.

Sphinx generates doc as several output formats from the reST text markup.

Sphinx

reSTreSTreStructuredTe

xt(reST)

reST Parser

HTML Builder

ePub Builder

LaTeX Builder texlive

HTMLtheme

Favorite Editor

12

Many docs are written by Sphinx

For examples

Python libraries/tools:Python, Sphinx, Flask, Jinja2, Django, Pyramid, SQLAlchemy, Numpy, SciPy, scikit-learn, pandas, fabric, ansible, awscli, …

Non python libraries/tools:Chef, CakePHP(2.x), MathJax, Selenium, Varnish

13

Relation between Sphinx and Docutils

Sphinx based upon Docutils library

Docutils/Sphinx supports reStructuredText it called reST, it's an extensible markup(PEP287 / accepted at 2002)

Sphinx

reST Parser

HTML Builder

ePub Builder

LaTeX Builder

HTMLtheme

docutils

14

Comparing Docutils and Sphinx

Docutils Sphinx

1. Single page

2. Link to others with full name

3. Cross reference in a page

4. Writers: html, latex, man, xml, ...

5. Standard reST markup specs

1. Multiple Pages

2. toctree to connect all pages under single tree structure

3. Cross reference over each other pages

4. Additional writers: html(w/ themes), pdf(latex), texinfo, epub, …

5. Additional markup specs:autodoc directive and so on

15

1. Sphinx introduction & Setup for i18n

Sphinx extensions (built-in)

Sphinx extensions extend reST markup and provide extra writers.

Sphinx provides these extensions to support automated API documentation.

sphinx.ext.autodocsphinx.ext.autosummarysphinx.ext.doctestsphinx.ext.coverage

Sphinx

reST Parser

HTML Builder

ePub Builder

LaTeX Builder

docutils

au

tosu

mm

ary

au

tod

oc

doctest

coverage

16

library code example

1. "utility functions"2.  3. def dumps(obj, ensure_ascii=True):4. """Serialize ``obj`` to a JSON formatted

``str``.5. """6. ...  

deep_thought/utils.py

$ tree /path/to/your-code+- deep_thought| +- __init__.py| +- api.py| +- calc.py| +- utils.py+- setup.py

17

$ pip install sphinx

Your code and sphinx should be in single python environment.

Python version is also important.

How to install Sphinx

18

$ cd /path/to/your-code$ sphinx-quickstart doc -m...Project name: Deep thoughtAuthor name(s): MiceProject version: 0.7.5......Finished

"-m" to generate minimal Makefile/make.bat-m is important to introduce this session easily.

How to start a Sphinx project

Keep pressing ENTER key

19

Create a doc directory

$ cd doc$ make html...Build finished. The HTML pages are in _build/html.

"make html" command generates html files into _build/html.

make html once

20

Current files structure

$ tree /path/to/your-code+- deep_thought| +- __init__.py| +- api.py| +- calc.py| +- utils.py+- doc| +- _build/| | +- html/ | +- _static/| +- _template/| +- conf.py| +- index.rst| +- make.bat| +- Makefile+- setup.py

Scaffold files

Build output

Library files

21

Generate API docs from your python source code

22

$ tree /path/to/your-code+- deep_thought| +- __init__.py| +- api.py| +- calc.py| +- utils.py+- doc| +- _build/| | +- html/ | +- _static/| +- _template/| +- conf.py| +- index.rst| +- make.bat| +- Makefile+- setup.py

1. import os2. import sys3. sys.path.insert(0,

os.path.abspath('..'))4. extensions = [5. 'sphinx.ext.autodoc',6. ]

setup autodoc extensiondoc/conf.py

23

Line-3: add your library path to import them from Sphinx autodoc.

Line-5: add 'sphinx.ext.autodoc' to use the extension.

Add automodule directive to your doc

1. Deep thought API2. ================3. 4. .. automodule:: deep_thought.utils5. :members:6.

1. "utility functions"2.  3. def dumps(obj, ensure_ascii=True):4. """Serialize ``obj`` to a JSON formatted

``str``.5. """6. ...  

doc/index.rst

24

deep_thought/utils.pyLine-4: automodule directive import

specified module and inspect the module.

Line-5: :members: option will inspects all members of module not only module docstring.

$ cd doc$ make html...Build finished. The HTML pages are in _build/html.

make html

25

How does it work?

autodoc directive generates intermediate reST internally:1. Deep thought API2. ================3.  4. .. py:module:: deep_thought.utils5.  6. utility functions7.  8. .. py:function:: dumps(obj, ensure_ascii=True)9. :module: deep_thought.utils10.  11. Serialize ``obj`` to a JSON formatted :class:`str`.

doc/index.rst

IntermediatereST

26

$ make html SPHINXOPTS=-vvv......[autodoc] output:

.. py:module:: deep_thought.utils

utility functions

.. py:function:: dumps(obj, ensure_ascii=True) :module: deep_thought.utils

Serialize ``obj`` to a JSON formatted :class:`str`.

You can see the reST with -vvv option

27

Take care!

Sphinx autodoc import your code

to get docstrings.

It means autodoc will execute code at module global level.

28

Danger code

1. import os2. 3. def delete_world():4. os.system('sudo rm -Rf /')5. 6. delete_world() # will be executed at "make html"

danger.py

29

execution guard on import

1. import os2. 3. def delete_world():4. os.system('sudo rm -Rf /')5. 6. delete_world() # will be executed at "make html"

danger.py

1. import os2. 3. def delete_world():4. os.system('sudo rm -Rf /')5. 6. if __name__ == '__main__':7. delete_world() # doesn't execute at "make

html"

safer.py

Execution guard

30

execution guard on import

1. import os2. 3. def delete_world():4. os.system('sudo rm -Rf /')5. 6. delete_world() # will be executed at "make html"

danger.py

1. import os2. 3. def delete_world():4. os.system('sudo rm -Rf /')5. 6. if __name__ == '__main__':7. delete_world() # doesn't execute at "make

html"

safer.py

Execution guard

31

"Oh, I can't understand the type of arguments and meanings even reading this!"

32

Lacking necessary information

1. def dumps(obj, ensure_ascii=True):2. """Serialize ``obj`` to a JSON formatted3. :class:`str`.4. 5. :param dict obj: dict type object to

serialize.6. :param bool ensure_ascii: Default is True. If7. False, all non-ASCII characters are

not ...8. :return: JSON formatted string9. :rtype: str10. """http://sphinx-doc.org/domains.html#info-field-

lists

"info field lists" for arguments

deep_thought/utils.py

33

def dumps(obj, ensure_ascii=True): """Serialize ``obj`` to a JSON formatted :class:`str`.

:param dict obj: dict type object to serialize. :param bool ensure_ascii: Default is True. If False, all non-ASCII characters are not ... :return: JSON formatted string :rtype: str """ ...

"info field lists" for arguments

deep_thought/utils.py

34

Cross-reference to functions

1. Examples2. ==========3. 4. This is a usage

of :func:`deep_thought.utils.dumps` blah blah blah. ...

examples.py

reference(hyper link)

35

Detect deviations of the impl and doc

36

Code example in a docstring

1. def dumps(obj, ensure_ascii=True):2. """Serialize ``obj`` to a JSON formatted3. :class:`str`.4. 5. For example:6. 7. >>> from deep_thought.utils import dumps8. >>> data = dict(spam=1, ham='egg')9. >>> dumps(data)10. '{spam: 1, ham: "egg"}'11. 12. :param dict obj: dict type object to

serialize.13. :param bool ensure_ascii: Default is True. If14. False, all non-ASCII characters are

not ...

deep_thought/utils.py

37

doctestblock

You can copy & paste the red lines from python interactive

shell.

Syntax highlighted output

$ make html...

rendereddoctestblock

38

You can expect that developers will update code examples when the interface is changed.

We expect ...

1. def dumps(obj, ensure_ascii=True):2. """Serialize ``obj`` to a JSON formatted3. :class:`str`.4. 5. For example:6. 7. >>> from deep_thought.utils import dumps8. >>> data = dict(spam=1, ham='egg')9. >>> dumps(data)10. '{spam: 1, ham: "egg"}'

The code example is very close from implementation!!

deep_thought/utils.py

39

 ̄\ _( ツ )_/  ̄

40

doctest builder

41

1. ...2. 3. extensions = [4. 'sphinx.ext.autodoc',5. 'sphinx.ext.doctest',6. ]7.

Line-5: add 'sphinx.ext.doctest' extension.

setup doctest extension

doc/conf.py

append

42

$ make doctest...Document: api-------------********************************************************File "api.rst", line 11, in defaultFailed example: dumps(data)Expected: '{spam: 1, ham: "egg"}'Got: 'to-be-implemented'...make: *** [doctest] Error 1

Result of "make doctest"

43

Result of doctest

Listing APIs automatically

44

Individual pages for each modules

api.html

calc.html

utils.html

45

1. deep_thought.utils2. ===================3. .. automodule:: deep_thought.utils4. :members:

doc/deep_thought.utils.rst

1. deep_thought.utils2. ===================3. .. automodule:: deep_thought.utils4. :members:

Add automodule directive to your doc

doc/deep_thought.utils.rst

1. deep_thought.calc2. ==================3. .. automodule:: deep_thought.calc4. :members:

1. deep_thought.api2. ==================3. .. automodule:: deep_thought.api4. :members:

doc/deep_thought.calc.rst

doc/deep_thought.api.rst

And many many reST files ... 46

 ̄\ _( ツ )_/  ̄

47

autosummary extension

48

1. ...2. 3. extensions = [4. 'sphinx.ext.autodoc',5. 'sphinx.ext.doctest',6. 'sphinx.ext.autosummary',7. ]8. autodoc_default_flags = ['members']9. autosummary_generate = True10.

Line-9: autosummary_generate = True generates reST files what you will specify with using autosummary directive.

setup autosummary extension

doc/conf.py

append

appendappend

49

1. Deep thought API2. ================3. 4. .. autosummary::5. :toctree: generated6. 7. deep_thought.api8. deep_thought.calc9. deep_thought.utils10.

Replace automodule with autosummary

doc/index.rst

$ make html...

50

Output of autosummary

autosummary directive become TOC for each module pages.

51

Discovering undocumented APIs

52

1. def spam():2. ...3. return res4. 5. def ham():6. ...7. return res8. 9. def egg():10. ...11. return res

How to find undocumented funcs?

doc/nodoc.py

53

coverage extension

54

1. ...2. 3. extensions = [4. 'sphinx.ext.autodoc',5. 'sphinx.ext.doctest',6. 'sphinx.ext.autosummary',7. 'sphinx.ext.coverage',8. ]9. autodoc_default_flags = ['members']10.autosummary_generate = True

setup coverage extension

doc/conf.py

append

Line-7: add 'sphinx.ext.coverage' extension.

55

make coverage and check the result

$ make coverage...Testing of coverage in the sources finished, look at the results in _build\coverage.

$ ls _build/coveragec.txt python.txt undoc.pickle

1. Undocumented Python objects2. ===========================3. deep_thought.utils4. ------------------5. Functions:6. * egg

_build/coverage/python.txt

This function doesn't have a doc!

56

CAUTION!

1. Undocumented Python objects2. ===========================3. deep_thought.utils4. ------------------5. Functions:6. * egg

python.txt

$ make coverage...Testing of coverage in the sources finished, look at the results in _build\coverage.

$ ls _build/coveragec.txt python.txt undoc.pickle

The command always return ZERO

coverage.xml is not exist

reST format for whom?

57

Let's make Pull-Request!

We are waiting for your contribution

to solve the problem.(bow)

58

Wrap-up

59

Why don't you write docstrings?

I don't know what/where should I write.Let's write a description, arguments and

doctest blocks at the next line of function signature.

Are there some docstring format spec?Yes, you can use "info field list" for argument

spec and you can use doctest block for code example.

It's not beneficial.You can use autodoc, autosummary,

doctest and coverage to make it beneficial.

60

Options, Tips

61

1. Options

62

Options for autodoc

:members: blahTo document just specified members. Empty is ALL.

:undoc-members: ...To document members which doesn't have docstring.

:private-members: ...To document private members which name starts with

underscore.

:special-members: ...To document starts with underscore underscore.

:inherited-members: ...To document inherited from super class.

63

2. Directives for Web API

64

sphinxcontrib-httpdomain 3rd-party ext

http domain's get directive

render

page

render routing table (index)

http highlighter

It also contains sphinxcontrib.autohttp.flask, .bottle, .tornado extensions

Listing

New Index

65

3. Document translation

66

Translation into other languages

$ make gettext...Build finished. The message catalogs are in _build/gettext.

$ sphinx-intl update -p _build/gettext -l zh_tw

#: ../../../deep_thought/utils.pydocstring of deep_thought.utils.dumps:1msgid "Serialize ``obj`` to a JSON formatted :class:`str`."msgstr " 序列化 ``obj`` 要 JSON 格式 :class:`str`."

msgid "For example:"msgstr " 例如 :"

locale/zh_tw/LC_MESSAGES/generated.po

language = 'zh_tw'locale_dirs = ['locale']

conf.py

$ make html...Build finished. The HTML pages are in _build/html. 67

Questions?@shimizukawa

68

Thanks :)

69