Descrevendo código no Sphinx¶
Nas seções anteriores do tutorial você pode ler como escrever documentação narrativa ou em prosa no Sphinx. Nesta seção você descreverá objetos código.
Sphinx oferece suporte à documentação de objetos código em várias linguagens, inclusive Python, C, C++, JavaScript e reStructuredText. Cada um deles pode ser documentado usando uma série de diretivas e de papéis agrupados por domínio. No restante do tutorial você usará o domínio Python, mas todos os conceitos vistos nesta seção também se aplicam aos outros domínios.
Python¶
Documentando objetos Python¶
O Sphinx oferece vários papéis e diretivas para documentar objetos Python, todos agrupados no domínio Python. Por exemplo, você pode usar a diretiva py:function
para documentar uma função Python da seguinte forma:
Creating recipes
----------------
To retrieve a list of random ingredients,
you can use the ``lumache.get_random_ingredients()`` function:
.. py:function:: lumache.get_random_ingredients(kind=None)
Return a list of random ingredients as strings.
:param kind: Optional "kind" of ingredients.
:type kind: list[str] or None
:return: The ingredients list.
:rtype: list[str]
Que será renderizado assim:
Observe várias coisas:
Sphinx analisou o argumento da diretiva
.. py:function
e destacou o módulo, o nome da função e os parâmetros apropriadamente.O conteúdo da diretiva inclui uma descrição de uma linha da função, bem como uma lista de campos de informações contendo o parâmetro da função, seu tipo esperado, o valor de retorno e o tipo de retorno.
Nota
O prefixo py:
especifica o domínio. Você pode configurar o domínio padrão para poder omitir o prefixo, seja globalmente usando a configuração primary_domain
, ou use a diretiva default-domain
para alterá-lo desde o ponto em que é chamado até o final do arquivo. Por exemplo, se você definir como py
(o padrão), você pode escrever .. function::
diretamente.
Referência cruzada de objetos Python¶
Por padrão, a maioria dessas diretivas geram entidades que podem ser referenciadas de qualquer parte da documentação usando o papel correspondente. Para o caso de funções, você pode usar py:func
para isso, da seguinte forma:
The ``kind`` parameter should be either ``"meat"``, ``"fish"``,
or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients`
will raise an exception.
Ao gerar a documentação do código, o Sphinx irá gerar automaticamente uma referência cruzada apenas usando o nome do objeto, sem que você precise usar explicitamente um papel para isso. Por exemplo, você pode descrever a exceção personalizada gerada pela função usando a diretiva py:exception
:
.. py:exception:: lumache.InvalidKindError
Raised if the kind is invalid.
Em seguida, adicione esta exceção à descrição original da função:
.. py:function:: lumache.get_random_ingredients(kind=None)
Return a list of random ingredients as strings.
:param kind: Optional "kind" of ingredients.
:type kind: list[str] or None
:raise lumache.InvalidKindError: If the kind is invalid.
:return: The ingredients list.
:rtype: list[str]
E por fim, o resultado ficaria assim:
Lindo, não é?
Incluindo doctests em sua documentação¶
Como agora você está descrevendo o código de uma biblioteca Python, será útil manter a documentação e o código o mais sincronizados possível. Uma das maneiras de fazer isso no Sphinx é incluir trechos de código na documentação, chamados doctests, que são executados quando a documentação é construída.
Para demonstrar doctests e outros recursos do Sphinx abordados neste tutorial, o Sphinx precisará ser capaz de importar o código. Para conseguir isso, escreva isto no início do conf.py
:
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here.
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
Nota
Uma alternativa para alterar a variável sys.path
é criar um arquivo pyproject.toml
e tornar o código instalável, para que ele se comporte como qualquer outra biblioteca Python. Entretanto, a abordagem sys.path
é mais simples.
Então, antes de adicionar doctests à sua documentação, habilite a extensão doctest em conf.py
:
extensions = [
'sphinx.ext.duration',
'sphinx.ext.doctest',
]
A seguir, escreva um bloco de doctest da seguinte forma:
>>> import lumache
>>> lumache.get_random_ingredients()
['shells', 'gorgonzola', 'parsley']
Doctests incluem as instruções Python a serem executadas precedidas por >>>
, o prompt padrão do interpretador Python, bem como a saída esperada de cada instrução. Dessa forma, o Sphinx pode verificar se a saída real corresponde à esperada.
Para observar como uma falha do doctest se parece (em vez de um erro de código como acima), vamos primeiro escrever o valor de retorno incorretamente. Portanto, adicione uma função get_random_ingredients
como esta:
def get_random_ingredients(kind=None):
return ["eggs", "bacon", "spam"]
Agora você pode executar make doctest
para executar os doctests da sua documentação. Inicialmente isso exibirá um erro, já que o código real não se comporta conforme especificado:
(.venv) $ make doctest
Running Sphinx v4.2.0
loading pickled environment... done
...
running tests...
Document: usage
---------------
**********************************************************************
File "usage.rst", line 44, in default
Failed example:
lumache.get_random_ingredients()
Expected:
['shells', 'gorgonzola', 'parsley']
Got:
['eggs', 'bacon', 'spam']
**********************************************************************
...
make: *** [Makefile:20: doctest] Error 1
Como você pode ver, o doctest relata os resultados esperados e reais, para facilitar o exame. Agora é hora de corrigir a função:
def get_random_ingredients(kind=None):
return ["shells", "gorgonzola", "parsley"]
E finalmente, make doctest
relata sucesso!
Porém, para grandes projetos, essa abordagem manual pode se tornar um pouco tediosa. Na próxima seção, você verá como automatizar o processo.
Outras linguagens (C, C++, outros)¶
Documentando e fazendo referência cruzada de objetos¶
O Sphinx também oferece suporte a documentação e referência cruzada de objetos escritos em outras linguagens de programação. Existem quatro domínios embutidos adicionais: C, C++, JavaScript e reStructuredText. Extensões de terceiros podem definir domínios para mais linguagens, como
Por exemplo, para documentar uma definição de tipo C++, você usaria a diretiva embutida cpp:type
, como segue:
.. cpp:type:: std::vector<int> CustomList
A typedef-like declaration of a type.
O que daria o seguinte resultado:
-
typedef std::vector<int> CustomList¶
Uma declaração semelhante a typedef de um tipo.
Todas essas diretivas geram referências que podem ser referenciadas usando o papel correspondente. Por exemplo, para fazer referência à definição de tipo anterior, você pode usar o papel cpp:type
da seguinte forma:
Cross reference to :cpp:type:`CustomList`.
O que produziria um hiperlink para a definição anterior: CustomList
.