Tuesday, March 10, 2020

Working with Sphinx Extensions and Building to DocFx Output


DocFx output using Sphinx .yml files and Sphinx extensions
DocFx output using Sphinx .yml files and Sphinx extensions

Overview


The previous Sphinx/DocFx posts are:

In those posts, we talked about the Sphinx conf.py configuration file for configuring Sphinx to use extensions. An extension is simply a Python module that can be used to extend Sphinx functionality. We used the "sphinx.ext.autodoc" extension to get docstring comments from Python files and we used the "docfx_yaml.extension" extension to instruct Sphinx to export YAML files. In the Sphinx to DocFx post, we had a conf.py file with:

extensions = ["sphinx.ext.autodoc",  "docfx_yaml.extension"]

In this post, we'll discuss two more extensions you can use so that the we have:

extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", 
              "sphinx.ext.extlinks", "docfx_yaml.extension"]

where:
sphinx.ext.autodoc Import modules for documentation, including pulling in content from docstrings.

docfx_yaml.extension An exporter for the Sphinx autodoc module to produce YAML files for use with DocFX. Seems to need to be at the end of the extensions list. Order is important. The docfx extension needs to be at the end. 
sphinx.ext.intersphinx Generate automatic links to the documentation in other projects like Python base classes. Depends on variable intersphinx_mapping variable in conf.py. See the interpret community repo for an example.

sphinx.ext.extlinks Allows creating a short link for commonly used links that go to subpages of one site.

sphinx.ext.intersphinx


The intersphinx_mapping configuration value is in the conf.py and it can be used to create mappings so that references to other documentation sets (outside of yours) can be referenced.

We'll use the following intersphinx_mapping:

intersphinx_mapping = {
    'Python': ('https://docs.python.org/3', None),
    'Pillow': ('https://pillow.readthedocs.io/en/latest/', None),
    'NumPy': ('http://docs.scipy.org/doc/numpy/', None),
    'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None),
    'sklearn': ('http://scikit-learn.org/stable', None),
    'matplotlib': ('http://matplotlib.sourceforge.net/', None)
}

To get a link to a Python builtin type at docs.python.org, we need only specify the type name in the type or rtype docstring field as follows:

"""
:type: bool
:rtype: list
"""

And the correct linkages will be made to the docset when Sphinx builds. And, if you wanted to link to these types in text outside of these fields? You can like so:

"""
This is a link to the Python built-in string type: :class:`str`.
"""

It works the same for other doc sets specified in the intersphinx_mapping.

"""
This is a link to :class:`pandas.DataFrame`, this to :mod:`matplotlib.image`,
and this to :func:`numpy.array`.

:type: pandas.DataFrame or numpy.array
:rtype: matplotlib.image
""" 

The big gotcha with using intersphinx_mapping is that if you look at the example above in the Sphinx rendered HTML, you would see pandas.DataFrame, matplotlib.image, and numpy.array are correctly linked to their library types for both the descriptive text and the :type: and :rtype: markup.
However, if you look in the DocFx rendered HTML (from the same docstring), you would see that while the links in the descriptive text do resolve, those in the :type: or :rtype: markup do not. To get around this problem, we need to use a cross reference file in DocFx as described here.  This will be the subject of a future post. (For numpy links, see the stack overflow question.)


This extension is a convenience for avoiding repeatedly typing a URL to a web site you are referencing frequently. The docs for extlinks show how you might use this to link back to GitHub issues. In our tutorial here, we'll create links back to Wikipedia.


In the conf.py file make sure you have extlinks configuration parameter defined like so:

extlinks = {'wiki': 
  ('https://en.wikipedia.org/wiki/%s','Wikipedia: ')
}

Now, suppose in a docstring you want to reference these three Wikipedia pages: https://en.wikipedia.org/wiki/Machine_learning, https://en.wikipedia.org/wiki/Supervised_learning, and https://en.wikipedia.org/wiki/Unsupervised_learning. Your docstring would look like this:

"""
Here are links using markup to make external links easier to
work with. See :wiki:`Machine_learning`, :wiki:`Supervised_learning`,
and :wiki:`Unsupervised_learning`.
"""

This docstring would create the three links like so: Wikipedia: Machine_learning, Wikipedia: Supervised_learning, and Wikipedia: Unsupervised_learning.

You can experiment with the presentation be modifying the extlinks configuration parameter.

Build Example


The steps below follow the post From Sphinx to DocFX - Generating Python API Documentation with DocFx. TIn this tutorial, our goal is to use the new extensions we enabled and show how they appear in the DocFX HTML. The prerequisites are:

  • Sphinx installed
  • DocFx installed
  • Optional: read or ran the previous tutorial

Step 1: Clone the repo travelmarx-blog and start in the \sphinx-extensions-example folder.

Step 2: Create config.py and index.rst files.

Use the instructions in the Sphinx Quickstart post to generate these files, following the suggested answers for the prompts. Or, if you already have these files from that project, you can reuse them here. Your folder structure should look like this.
.
├───build
├───mycode
│   ├───core_api
│   │   ├───package1
│   │   └───package2
│   └───test_api
└───source
    ├───_static
    └───_templates

Step 3: Edit source\config.py to include extensions.

In this tutorial, we are working with four of the extensions mentioned in the intro:

extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 
   'sphinx.ext.extlinks', 'docfx_yaml.extension']

Point to the code folder:

import os
import sys
sys.path.insert(0, os.path.abspath('../mycode'))

Add intersphinx_mapping configuration:

intersphinx_mapping = {
    'Python': ('https://docs.python.org/3', None),
    'Pillow': ('https://pillow.readthedocs.io/en/latest/', None),
    'NumPy': ('http://docs.scipy.org/doc/numpy/', None),
    'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None),
    'sklearn': ('http://scikit-learn.org/stable', None),
    'matplotlib': ('http://matplotlib.sourceforge.net/', None)
}

Add extlinks configuration:

extlinks = {'wiki': 
  ('https://en.wikipedia.org/wiki/%s','Wikipedia: ')
}

Some of these lines in the config.py file may already exist and you'll have to uncomment them and/or modify them.

Step 4: Run sphinx-apidoc to create .rst (reStructuredText) files describing the code.

In the root folder, run:

sphinx-apidoc -o source .\mycode

This creates .rst files in the \source folder.

Step 5: Modify the source\index.rst file to include the modules.rst file which is the entry point for code to document.

Running sphinx-apidoc will produce a source\modules.rst file by default. The modules.rst file is the entry point for documenting the code in \mycode.

source\index.rst (snippet, add the "modules" line)
Test documentation
=======================
.. toctree::
    :maxdepth: 4
    :caption: Table of Contents  

    modules

Step 6: Run sphinx-build to create Sphinx HTML and create YAML files for DocFx.

In the root folder, run

sphinx-build source build

You may not be interested in Sphinx's HTML, but it's this step which creates the YAML files. It doesn't hurt to check it to see what Sphinx's HTML looks like. To see it, run:

build\index.html

If you are interested in only the Sphinx generated HTML, then you can stop here.

Step 7: Confirm the YAML files were generated.

Starting in root folder, run:

dir build\docfx_yaml

You should see a listing of .yml files like "core_api.package1.someclass.SomeClass.yml".

Step 8: Generate an initial docfx.json file.

In the root folder, run:

docfx init -q

This will create a docfx_project folder with the docfx.json configuration file.

Step 9: Copy the Sphinx YAML files to the docfx_project folder.

Copy .\build\docfx_yaml\* to .\docfx_project\api\*

Step 10: Build the DocFx HTML and serve the docs on localhost.

Starting in the root folder, run:

docfx docfx_project\docfx.json --serve

Step 11: View the HTML docs produced by DocFx.

Go to http://localhost:8080.

You will note that there are some warnings in the DocFx output for things to address. Mostly invalid links.

Build Cycle


So running through the build once is great, but what about an authoring flow that works for the fix-build-verify cycle. Well that's where it gets messy with both Sphinx and DocFx we've found. We find that cleaning out previous build artifacts and building everything works the best. To this end we create a simple batch file (for Windows) that looks like the following:

REM For best results cleaning out directories works the best

del build\* /Q
del build\docfx_yaml\* /Q
rmdir docfx_project\_site /S /Q
rmdir docfx_project\obj\.cache /S /Q

del source\core_*.rst /Q
del source\modules.rst /Q
del source\test_*.rst /Q

REM Run sphinx commands

sphinx-apidoc -o source .\mycode -f
sphinx-build -a source build

REM Copy the sphinx generated yaml files to the docfx folder

copy .\build\docfx_yaml\* .\docfx_project\api\*

REM Build and serve docfx html
docfx docfx_project\docfx.json --serve


Monday, March 9, 2020

Bergamo – Coronavirus – Street Sign Language Lesson XXIX

previous lesson | this lesson | next lesson

In this post of the Street Sign Language Lesson series, we find ourselves in the middle of the coronavirus pandemic. Here in Italy, and specifically Lombardia, the effect has been devastating.

In the many competing sources of information, the Regione Lombardia - the entity governing the region of Lombardy - has put information to help residents make the right choice and understand their responsibility for keeping the virus at bay. In this post, we'll look at eight of those messages from the point of view of a language lesson. These informational message can be found inTwitter with #fermiaologinsieme and at the Regione web site: https://coronavirus.regione.lombardia.it/


Se ti vuoi bene, lavati le mani

Se ti vuoi bene, lavati le mani. Piu del solito. Meglio di solito. 
"If you love (care about) yourself, wash your hands. More than usual. Better than usual."

Many of the messages in this campaign start with the the se ti vuoi bene - "if you loved yourself" - to drive home the message that's this is a personal fight against the coronavirus. The informal imperative lava plus the reflexive pronoun ti come together as làvati meaning "wash your hands". We added the accent on à to indicate the stress. It's not lavàti

Se ti vuoi bene, metti la mascherina

Se ti vuoi bene, metti la mascherina. Protegge te e gli altri.
"If you love yourself, use a mask. It protects you and others."

We were initially tempted to use the word maschera ("mask") when talking about protection, but it's really mascherina ("half mask") that is more appropriate. Also a little tricky is protegge te, which you might read quickly as protect yourself (following with imperatives that have gone before), but it's really "(the mask) it protects you".



Se ti vuoi bene, mantieni la distanza

Se ti vuoi bene, mantieni la distanza. Un metro può bastare.
"If you love yourself, keep your distance. A meter is enough."


Se ti vuoi bene, rimani a casa

Se ti vuoi bene, rimani a casa. Metti il virus alla porta.
"If you love yourself, stay at home. Kick out the virus."

The interesting phrase here is mettere alla porta, which has the idea of keeping away, chasing way, showing the door, or firing. The Italian Wikipedia page of frasi fatti - clichés or platitudes - describes mettere alla porta as allontonare ("push away"), licenziare ("fire, dismiss"), cacciare senza tanti complimenti ("chase away unceremoniously").  All appropriate for the virus.


Se vuoi bene ai noni, tienili a casa

Se vuoi bene ai nonni, tienili a casa. Difendi chi e più fragile.
"If you love your grandparents, keep them at home. Defend those that are more fragile."

This one pulls at your heartstrings. Think of your grandparents and keep them - masculine plural - at home so you use li, a direct object reference as "them". Tieni is the informal imperative of tenere.


Tutela te, proteggi gli altri

Tutela te, proteggi gli altri
"Protect yourself, protect others."

Tutela is the imperative form of the verb tutelare, to protect or defend. We have the English word tutelage - rarely used - which means the education or protection of someone.


Isoliamo il virus

Isoliamo il virus. Evitare lughi affollati. Mantenere 1 m di distanza. Evitare il contatto fisico. Over 65 stare a casa ed evitare estranaei
"We isolate the virus by avoiding crowded places, maintaining a distance of 1 meter, avoiding physical contact, and those over 65 stay at home and avoid strangers."

This is a list of things to do to avoid the catching (or spreading) the virus. Since this is a list of instructions, the imperative form using the infinitive form of the verb is use: evitare, mantenere, and stare.

Keep calm and mantieni le distanze

Keep clam and mantieni le distanze. Cornovirus. Fermiamolo insieme
"Keep calm and keep your distances. Coronavirus. We can stop it together."

This message is a take off the British motivational poster during the lead up to World War II, Keep Calm and Carry On. Note that since virus in Italian is masculine, we use the masculine article: "fermiamolo".



Sunday, March 8, 2020

A Sunday Walk To Maresana


View from Villa Pighet looking west toward Val Seriana.View over Sorisole.
Left: View from Villa Pighet looking west toward Val Seriana. Right: View over Sorisole.

Overview

Length: ~ 15 km (9.3 miles)
Duration: < 5 hours (includes at least an hour for lunch
Elevation:  max 700 m (2300 ft), min 300 m (985 ft)
Location: Italy, Lombardia, Bergamo, Colli di Bergamo


The coronavirus has arrived and we are in lockdown. Well, to be honest, the virus didn't just arrive but has been here a for at least a few weeks. Yesterday, the news of the lockdown in northern Italy added another blow to the psyche of folks living here. Lombardy in it's entirety (which includes Bergamo) is in that lockdown area. Everyone is a bit on edge. We took to the hills.

The hills around Bergamo are - as we've written many many times - are one of the great assets of living in Bergamo. And today was no different, helping us take our minds off the virus. We just walked out with no specific plan in mind and ended up above Maresana and had a nice lunch at Trattoria del Moro (Ponteranica).

Directions


Our starting location is usually Fontana Delfino (Piazzetta del Delfino) in the lower city. By the way, the fountain won the most votes in a Sanex-Palmolive competition to be restored and is currently under restoration. Can't wait to see it brought back to life and working again!

From the fountain, walk to the Accademia Carrara and on to the Atalanta Stadium (officially called the Gewiss Stadium). The stadium is in the middle of renovations as well. They have finished one part of the renovation so far - the north end called "curva nord". The result looks like the former stadium much "puffed" up with lots of additional space for wonderful commercial activities like Burger King and American Graffiti restaurants. Sigh.

Closing your eyes past the puffed up stadium, you head north. You then can continue on to Quintino Alto to pick up trail 533, which is the most common way to climb up to Maresana. Today we follow an unofficial trail that follows the spine of the hill between Bergamo and Torre Boldone/Ranica. The trail starts with via Delizia and turns from asphalt to dirt. Along the spine is a series of roccoli - ancient bird trapping devices - until you reach via Maresana (here). From there, we decide to take a look at the Ristorante Villa Pighet (here), passing by a large white cross on Colle di Ranica.

Outside of Pighet, we chatted with an Italian family (husband, wife, son). We talked about how the virus and closures of schools has made experimenting with new ways of teaching, such as online tools, an urgent priority. It's starting to happen but it will take time. Even teleworking in Italy is not as common as it is in the USA. One of the main reasons is that many jobs still require physical presence to do the work.

The family we chatted with mentioned Trattoria del Moro (here) and so we headed off to see if we could get in for lunch and we were successful. We had a great lunch

Trattoria del Moro - appetizer.Trattoria del Moro - pasta (foiade) with rabbit.
A lunch at Trattoria del Moro. Appetizer, foiade with a rabbit sauce and polenta with beef cooked in Barolo wine.

Back to the virus. Before lunch, we washed our hands and the staff is maintaining lost of distance between tables. There is even red tape on the floor indicating how far to stand back from the bar when approaching to pay your bill. By law, they have to ensure a minimum distance of one meter.

From the trattoria, it was back to Maresana and then down trail 533 and into town past Gewiss stadium, up via San Tomaso, and back to Fontana del Delfino.

We did the reverse of this hike back in March 2017, Walk from Bergamo to Maresana for Lunch.

Flora


Here are a few plants we saw along the way. Two interesting plants flowers today are the Erythronium dens-cans and the Polygala chamaebuxus, because they are less common in this area.

[Family] Genus species – {Common name in Italian}

[Apocynaceae] Vinca major – {Pervinca maggiore}
[Asteraceae] Bellis perennis – {Pratolina comune}
[Boraginaceae] Pulmonaria sp. – {Polmonaria}
[Euphorbiaceae] Euphorbia helioscopia – {Euforbia calenzuola} - This is just a guess on the species.
[Liliaceae] Erythroninum dens-canis – {Dente di cane}
[Polygalaceae] Polygala chamaebuxus – {Poligala falso bosso}
[Primulaceae] Primula vulgaris – {Primula comune}
[Ranunculaceae] Anemone nemorosa – {Anenome bianca}
[Ranunculaceae] Hepatica nobilis {Erba trinità}
[Violaceae] Viola odoratum – {Viola mammola}

For more information on identifying plants, see the post Resources for Identifying Plants around Bergamo.
  
[Apocynaceae] Vinca major – {Pervinca maggiore}[Boraginaceae] Pulmonaria sp. – {Polmonaria}[Ranunculaceae] Hepatica nobilis {Erba trinità}[Violaceae] Viola odoratum – {Viola mammola}
Left to right: Vinca, Pulmonaria, Hepatica, and Viola.

[Asteraceae] Bellis perennis – {Pratolina comune}[Euphorbiaceae] Euphorbia helioscopia – {Euforbia calenzuola} - This is just a guess on the species.[Liliaceae] Erythroninum dens-canis – {Dente di cane}[Liliaceae] Erythroninum dens-canis – {Dente di cane}
Left to right: Bellis, Euphorbia, and Erythronium.

[Polygalaceae] Polygala chamaebuxus – {Poligala falso bosso}[Polygalaceae] Polygala chamaebuxus – {Poligala falso bosso}[Primulaceae] Primula vulgaris – {Primula comune}[Ranunculaceae] Anemone nemorosa – {Anenome bianca}
Left to right: Polygala (magenta colored), Polygala (yellow), Primula, Anemone.

East of Bergamo heading toward Torre Boldone.East of Bergamo heading toward Torre Boldone.A map of today's walk - to Maresana.Climbing up through a roccolo - near Bergamo.
Left and center left: East of Bergamo heading toward Torre Boldone. Center right: A map of today's walk. Right: Climbing up through a roccolo.

Tuesday, February 25, 2020

From Sphinx to DocFX - Generating Python API Documentation with DocFx

Overview


In a previous post Sphinx Quickstart, we covered a very basic setup of Sphinx. In this post, we go farther and talk about Sphinx DocFX YAML, an exporter for the Sphinx Autodoc module. Our goal is to produce YAML files that can be consumed by DocFX, a documentation generator for .NET that also converts YAML files to HTML. Many doc sets at https://docs.microsoft.com/ are generated with DocFX, including Python doc sets that use Sphinx to generate YAML, which is then converted to HTML with DocFX.

Terminology


To understand how to go from Sphinx to DocFX using the Sphinx DocFX YAML exporter, we need to break down some of the terms used...or at least we did to make sense of it all.

Sphinx

  • Sphinx is a documentation generator, it was originally created for Python documentation, but can be used for a range of languages.
  • Sphinx uses reStructuredText (rST) as its markup language. Sphinx's utility comes from the power and straightforwardness of reStructuredText (reST) and its parsing and translating suite, Docutils. reST is used both in .rst files and in docstrings in .py files.
  • In the Quickstart, we created an example reSt file (foo.rst) and built HTML documentation from it. 
  • Autodoc is an extension for Sphinx. (Sphinx is extensible to support the needs of different projects. An extension is simply a Python module.)
  • Autodoc adds directives like "autofunction" and "automodule". These directives determine what API is used to generate docs.
  • When using the autodoc extension (added in the conf.py file) with Sphinx, you are including documentation from Python docstrings. A Python docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the __doc__ special attribute of that object.
  • When you run the command sphinx-build (or make html if it was created), Sphinx autodoc generates the API documentation for your Python project code using the index.rst (this is the default name, but it can be any name you want). Sphinx imports the code via standard Python import mechanisms, and then generates the proper reST in the Sphinx Python domain. The reST files are then parsed to create doctree files used internally in Sphinx to generate HTML. If you only want the HTML output from Sphinx (and not DocFX), then you can stop here. This is the point at which the post Sphinx Quickstart stops.

sphinx-build

  • Usage: sphinx-build [options] <sourcedir> <outdir> [filenames...]
  • (If you ran sphinx-quickstart, you had the option of creating a make file so that you can just type make html instead of sphinx-build.)
  • This command creates documentation from files in <sourcedir>and places HTML in <outputdir>.
  • This command command looks for <sourcedir>/conf.py for configuration settings.
  • This command creates documentation in different formats. A format can be specified on the command line, otherwise it defaults to HTML. (Check the conf.py file if in doubt.)
  • By default, everything that is outdated is built. Output only for selected files can be built by specifying individual filenames.
  • Since Sphinx has to read and parse all source files before it can write an output file, the parsed source files are cached as “doctree pickles”. Normally, these files are put in a directory called .doctrees under the build directory.
  • If you didn't run the Sphinx Quickstart and don't have an index.rst file to start with, then you could use the sphinx-apidoc command to create module .rst files that would be equivalent to index.rst.

Sphinx DocFx YAML

  • Sphinx DocFX YAML is an exporter for the Sphinx Autodoc module that produces YAML files adhering to the DocFX YAML metadata specification. For more information, see readthedocs.
  • DocFX YAML describes language metadata for programming languages. The main user scenario for language metadata is to generate reference documentation. Specifically, we can use the YAML as input to DocFX and let DocFX generate HTML.
  • YAML files represent the API documentation. Example.
  • DocFX stands for Document Frameworks. To use it, add the extension to the source\conf.py file like so:

    extensions = ['sphinx.ext.autodoc', 'docfx_yaml.extension']
  • With exporter added to conf.py, use Sphinx DocFx as usual by running the command make html.
DocFX

  • DocFX generates API reference documentation from triple-slash comments in C#\VB  source code. Or, it can consume YAML files and render them as HTML.
  • It also allows you to use Markdown files to create additional topics such as tutorials and how-tos, and to customize the generated reference documentation.
  • The punchline is this: From a Python project using Autodoc and SphinxDocFX YAML exporter, you can generate YML files to be used with DocFX. This is what the example below does.
  • Why? Because HTML generated from DocFX has a number of benefits beyond the HTML generated from Sphinx, including API cross referencing, generating from markdown files (.md) alongside API reference, customizable themes and templates.  

An Example


Prerequisites:


Step 1: Clone the travelmarx-blog repo and start in the sphinx-docfx-example directory.

sphinx-docfx-example folder is the root folder. in subsequent steps. You should have the following:
.
└───mycode
    ├───core_api
    │   ├───package1
    │   └───package2
    └───test_api

Step 2: Create config.py and index.rst files.

See the Sphinx Quickstart for information about running the sphinx-quickstart command. Your folder structure should look like this.
.
├───build
├───mycode
│   ├───core_api
│   │   ├───package1
│   │   └───package2
│   └───test_api
└───source
    ├───_static
    └───_templates

Step 3: Edit source\config.py.

Configure the extensions:
extensions = ['sphinx.ext.autodoc', 'docfx_yaml.extension']
Point to the code folder:
import os
import sys
sys.path.insert(0, os.path.abspath('../mycode'))
Some of these lines in the config.py file may already exist and you'll have to uncomment them.

Step 4: Run sphinx-apidoc to create .rst (reStructuredText) files describing the code.

Starting in the sphinx-docfx-example (root) folder, run:
sphinx-apidoc -o source .\mycode
This creates .rst files in the \source folder.

Step 5: Modify the source\index.rst to include modules to document.

Running sphinx-apidoc will produce a source\modules.rst file by default. The modules.rst file is the entry point for documenting the code in \mycode.

source\index.rst (snippet, add the part in red)

Test documentation
=======================
.. toctree::
    :maxdepth: 4
    :caption: Table of Contents
 
    modules

Step 6: Run sphinx-build to create Sphinx's HTML.
sphinx-build source build
Besides building the Sphinx HTML (which you may not care about), this also creates .yml files in the \build\docfx_yaml folder. These will be used in a later step with DocFx.

To view the Sphinx HTML, starting in the root folder, run:
build\index.html
For comparison with docFx HTML (which is generated in Step 7), here is the Sphinx-generated HTML:



Step 7: Confirm that YAML files were generated.

Starting in root folder, run:
dir build\docfx_yaml
You should see a listing of .yml files like "core_api.package1.someclass.SomeClass.yml".

Step 8: Generate an initial docfx.json file.

Starting in the root folder, run:
docfx init -q
This will create a docfx_project folder with the docfx.json configuration file.

Step 9: Copy the Sphinx YAML files to the \docfx_project folder.

Copy .\build\docfx_yaml\* to .\docfx_project\api\*

Step 10: Build the DocFx HTML and serve the docs.

Starting in docfxtest folder, run:
docfx docfx_project\docfx.json --serve

Step 11: View the HTML docs produced by DocFx.

Go to http://localhost:8080.



Some points to note:

  • The difference in the look between Sphinx HTML and DocFx HTML. Both can be customized as needed.
  • How the link to "AnotherClass" is an active link in the DocFX screenshot. This is one of the benefits of using DocFx, cross reference linking.
  • We didn't add any "Articles" (.md files) but that is also a nice feature of DocFx, to integrate API and conceptual (articles) docs. For example of how that could be done, see our Scrapbook101core site.
  • On subsequent runs through the steps above (say, if you changed a docstring in the code), you will typically:
    • delete content in \build folder
    • run steps 6, 9, and 10.

Next Steps:

  • Customize docfx.json file.
  • Read up on cross-linking with DocFx.
  • Add other markdown files (.md) along with API docs.



Monday, February 24, 2020

Sphinx Quickstart


Generate files


The instructions here are for Windows. With slight modifications, they can be applied to other platforms. The code for this post is at https://github.com/travelmarx/travelmarx-blog/tree/master/sphinx-quickstart.

Make sure you have Sphinx installed, then clone the travelmarx-blog repo to your local environment. Starting in the sphinx-quickstart directory you should have the following:

> tree
│   .gitignore

└───mycode
        myclasses.py
        __init__.py

Run the Sphinx quickstart command.

> sphinx-quickstart

Accept defaults for everything except these parameters.
  • Separate source and build directories (y/n) [n]: Y
  • Project name: MyTestDocs
  • Author name(s): your-alias
  • autodoc: automatically insert docstrings from modules: (y/n) [n]: Y 
The last setting for configuring autodoc is important. When answering the quickstart questions, it can be easy to accept the default for this setting which is not to install it. The autodoc extension is configured in the source\conf.py file like so:

extensions = ['sphinx.ext.autodoc']

Build the HTML. The command make html is a convenience for running the command sphinx-build -b html sourcedir buildir. The make file assumes current directory is source directory, and it creates the build directory "build". HTML is the default doc type produced.

> make html
> tree

> tree
├───build
│   ├───doctrees
│   └───html
│       ├───_sources
│       └───_static

├───mycode
└───source
    ├───_static
    └───_templates

Open the docs.

> build\html\index.html

At this point you have basically a framework to build on, but not much else. The index.html page should look like this.


The index.rst file


In the sphinx-quickstart\source folder there should be an index.rst file. Edit the file to add the automodule to automatically document members of a module myclasses.py.

> type index.rst
.. MyTestDocs documentation master file, created by
   sphinx-quickstart on Thu Jun 20 14:06:30 2019.
   Adapt this file to your liking, but it should at least
   contain the root `toctree` directive.

Welcome to MyTestDocs's documentation!
======================================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

.. automodule:: myclasses
   :members:

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

The index file is the initial documentation file. You can see that lines in the index.rst appear in the index.html shown above. The index.rst file can contain reStructuredText documentation and directives (the same that appear in Python docstrings). In the example above, we are using automodule to indicate that the docstrings in myclasses should be documented.

Add test Python code


If you cloned the repo, you should have the following:

  • mycode\myclasses.py module.
  • mycode\__init__.py file, which signals that the directory contains a package.


Edit the source\config.py to so that Sphinx can find the code. Here are the lines:

import os
import sys
sys.path.insert(0, os.path.abspath('../mycode'))

Make sure the import lines are not commented out, i.e., have a "#" in front of them.

Here are the two files __init__.py and myclasses.py.

mycode\__init__.py
import myclasses

mycode\myclasses.py
class SimpleClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

class Person:
    """Creates a Person based on name and age."""
    def __init__(self, name, age):
        self.name = name
        self.age = age


Back in the root folder of sphinx-quickstart, rebuild:

> make html

The output should look something like this, which includes docstrings in myclasses.py:


If your code folder is outside the "doctest" folder, make changes to the os.path.abspath in the conf.py file as appropriate.

Your final directory structure should look like this:

>tree
├───build
│   ├───doctrees
│   └───html
│       ├───_sources
│       └───_static
├───mycode
│   └───__pycache__
└───source
    ├───_static
    └───_templates


reStructuredText


Let's add a little more functionality to this quickstart. Suppose we have a file foo.rst that contains documentation we want to include as well. Then we can add foo.rst and make sure it is documented by adding a reference to foo in index.rst:

source\foo.rst

foo module
==========

This is the foo module description.

.. note::

   This is a note.

source\index.rst  (changed part in red)

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   foo

.. automodule:: myclasses
   :members:

The syntax you can use inside of foo.rst is described in Sphinx reStructuredText. At this point in our quickstart, we have HTML generated documentation with some content coming from reStructuredText in an .rst file and some content coming from reStructuredText in docstrings in .py files.

Build the docs again:

> make html

Notice that "foo" appears in doc contents.

If interested, go to the next post in the series: From Sphinx to DocFX - Generating Python API Documentation with DocFx.

Tuesday, December 31, 2019

One Hundred (and More) Common Italian Nouns Derived from Verbs : Confusion Over Masculine and Feminine



The Argument


In the Italian language, nouns can derive from verbs. Verbs can derive from nouns. Also, nouns can derive from adjectives and vice-versa, and much more. It’s a circus of possible conversions. But in this circus, we’d like to call out one specific category because it’s a place we trip up a lot, and that category contains nouns that resemble the first or third-person singular conjugation of a related verb in present indicative.


For example, there is the noun il disprezzo (contempt) and the related verb disprezzare (to despise or scorn). The first-person conjugation of the verb is io disprezzo. Now consider, the noun la sfida (challenge) and the related verb sfidare (to challenge). In this case, the related noun is feminine. The third-person conjugation is lui/lei sfida. This is where we trip up: remembering if the associated noun is masculine or feminine.


We say “related” verb because we aren't sure if the verb came first or the noun. When looking up these nouns in WordReference, the related verb is indicated, but the true etymology is not clear. Spot checking a few noun/verb combinations using Treccani definitions reveals that it's not a simple as saying these nouns derive from the related verbs as it could easily be the other way around, case in point: pranzare from pranzo. The derivations of the words are complicated and better left to linguists. That said, the main point of this post remains valid: there can be confusion between a noun and its related verb, and in particular knowing the gender of the noun.

In the list below, you’ll notice that all the verbs are -ARE verbs. The noun is formed by removing the -ARE and adding the letter O or A. Verbs ending in -ERE and -IRE and their related nouns derive from the past participle usually. For example, la discesa from discendere and la promessa from promettere. We are not talking about these nouns here; we are concerned only with the -ARE verbs.

In English, do we have nouns derived from verbs? Yes we do, and they are called verbal nouns, and they are mostly related to gerunds. There are also nouns created by adding to verbs. See for example, Forming Agent Nouns from Verbs. But these cases in English are not like the Italian cases discussed here where we remove part of verb root and add the letter O or A to form a noun that looks like a conjugation.

From the Treccani reference on conversion, we can get a clue to how to resolve to problem of the ending and knowing if it is O or A. The reference says (paraphrasing) that the major part of the nouns derived from verbs are masculine (end in O). And furthermore, they are usually action verbs and end in -ARE. Many them are verbs ending in -EGGIARE. Here’s the text from the reference:


“A partire da verbi si formano principalmente nomi. La conversione è segnalata da un cambio di classe flessiva (es. arrivare -> arrivo, parcheggiare -> parcheggio, ricoverare -> ricovero). Il maggior numero di nomi è costituito da maschili in -o, i quali di norma sono nomi di azione (-> azione, nomi di) e le regolari estensioni semantiche tipiche di questa classe (risultato dell’azione: disegno, taglio; strumento: cambio; luogo: incrocio). I verbi appartengono quasi esclusivamente alla coniugazione in -are; fra quelli derivati prevalgono quelli con il suffisso -eggiare (conteggiare -> conteggio, palleggiare -> palleggio, pareggiare -> pareggio).”

Of course, we wondered what percentage are masculine. What are our odds of being correct if we guess masculine? To find out, we created a list and counted. The list is composed of words that we encounter daily, so it isn’t in necessarily in any particular order. It would be useful to try find the most frequently used nouns in this category (-ARE verbs with related noun), but we did not do that here.


Of the 100+ nouns (149 to be precise) and their related -ARE verbs in the list below, 39 are feminine (17%) and 110 (83%) are masculine. So, our advice is to guess masculine when in doubt.


There are exceptions to our simplified argument; here are a few that we noted:

  • il pari => parare - masculine noun ending in I.
  • il sistema => sistemare and il programma => programmare - masculine nouns but ending in A.

These exceptions are not included in the list below.

If both a masculine and feminine noun derive from the verb (or seem to - never can be sure as noted above), then both nouns are included in the list below. Examples are la scherma/lo schermo => schermare and il trombo/la tromba => trombare.

Finally, getting back to our circus of possible conversions, the site Valico.org site has a table of morphemes that gives an idea of how nouns are derived from verbs (nomi deverbali), adjectives derive from verbs (aggettivi deverbali), adjectives derive from nouns (aggettivi denominali), verbs derive from nouns (verbi denominali), nouns derive from other nouns (nomi denominali), verbs derive from adjectives (verbi deaggettivali), nouns derive from adjectives (nomi deaggettivali), and adverbs derive from adjectives.

The List


noun => verb  (blue is feminine)

l'abbraccio => abbracciare
l'accumulo => accumulare
l'annucio => annuciare
l'arringa => arringare
l'arrivo => arrivare
l'assaggio => assaggiare
l'attacco => attaccare
l'avvio => avviare
la bada => badare
il bacio => baciare
il ballo => ballare
il balzo => balzare
il beccheggio => beccheggiare
il bisogno => bisognare
il bisticcio => bisticciare
il blocco => bloccare
la caccia => cacciare
il calcio => calciare
il calcolo => calcolare
il cambio => cambiare
il cammino => camminare
il cancello => cancellare
il carico => caricare
la carrucola => carrucolare
la consegna => consegnare
il conteggio => conteggiare
la conquista => conquistare
il continuo => continuare
la copia => copiare
il costo => costare
la cura => curare
la denucia => denuciare
il deposito => depositare
il decreto => decretare
il disegno => disegnare
il disprezzo => disprezzare
il dondolo => dondolare
il dono => donare
la fatica => faticare
la fattura => fatturare
il fermo => fermare
la firma => firmare
la folla => follare
il fracasso => fracassare
il freddo => freddare
la frusta => frustare
la fuga => fugare
la fustella => fustellare
il galoppo => galoppare
il ghiaccio => ghiacciare
il girovago => girovagare
il guardo => guadare
l'impegno => impegnare
l'inchino => inchinare
l'incrocio => incrociare
l'inganno => ingannare
l'ingorgo => ingorare
l'inzio => inziare
il lancio => lanciare
la macchia => macchiare
la maschera => mascherare
la molla => mollare
la nota => notare
il nuoto => nutoare
l'obbligo =>obbligare
l'ombra => ombrare
l'ostacolo => ostacolare
il palleggio => palleggiare
il parcheggio => parcheggiare
il pareggio => pareggiare
il parto => partire
il pascolo => pascolare
il passeggio => passeggiare
il passo => passare
il pasticcio => pasticciare
la pausa => pausare

il pellegrino => pellegrinare
il pericolo => pericolare
il poggio => poggiare
il pranzo => pranzare
la prova => provare
il pugno => pugnare
il quadro => quadrare
il rammarico => rammaricare
il rampollo => rampollare
il raggio => raggiare
il registro => registrare
la ricerca => ricercare
il ricovero => ricoverare
il rientro => rientrare
il rimbalzo => rimbalzare
il rischio => rischiare
il risparmio => risparmiare
il rispetto => rispettare
il ritorno => ritornare
il rombo => rombare
il saldo => saldare
il salto => saltare
lo sbadiglio => sbadigliare
la scherma => schermare
lo schermo => schermare
lo scherzo => scherzare
lo schiaffo => schiaffare
la scopa => scopare
lo scopo => scopare
lo scoppio => scoppiare 
la scoreggia => scoreggiare
la scorta => scortare
lo sdegno => sdegnare
la sfida => sfidare
lo sfoggio => sfoggiare
lo sforzo => sforzare
la slitta => slittare
il soggiorno => soggiornare
il sogno => sognare
la sosta => stostare
lo sparo => sparare
la spesa => spesare
lo spiazzo => spiazzare
la stiva => stivare
lo straccio => stracciare
lo strappo => strappare
lo strumento => strumentare
la sveglia => svegliare
la taccia => tacciare
il taglio => tagliare
la taglia => tagliare

il tappo => tappare
il tarlo => tarlare
la tassa => tassare
il tasto => tastare
il telefono => telefonare
il timbro => timbrare
il tocco => toccare
il tonfo => tonfare
la traccia => tracciare
il tratto => trattare
il trapano => trapanare
il trasloco => traslocare
il trombo => trombare
la tromba => trombare

il trucco => truccare
il tuono => tuonare
il valico => valicare
il vincolo => vincolare
l'urlo => urlare
il volo => volare

References

[1] Treccani - nomi-deverbali
[2] Treccani - conversione
[3] https://en.wikipedia.org/wiki/Verbal_noun
[4] http://www.valico.org/lista_morfemi.pdf