9

I have a question regarding the Sphinx autodoc generation. I feel that what I am trying to do should be very simple, but for some reason, it won't work.

I have a Python project of which the directory is named slotting_tool. This directory is located at C:\Users\Sam\Desktop\picnic-data-shared-tools\standalone\slotting_tool

I set up Sphinx using sphinx-quickstart. Then my directory structure (simplified) is as follows:

slotting_tool/
|_ build/
|_ source/
|___ conf.py
|___ index.rst
|_ main/
|___ run_me.py

Now, I set the root directory of my project to slotting_tool by adding the following to the conf.py file.

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

Next, I update my index.rst file to look like this:

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

.. automodule:: main.run_me
   :members:

When trying to build my html using the sphinx-build -b html source .\build command, I get the following output, with the no module named error:

(base) C:\Users\Sam\Desktop\picnic-data-shared-tools\standalone\slotting_tool>sphinx-build -b html source .\build
Running Sphinx v1.8.1
loading pickled environment... done
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 1 source files that are out of date
updating environment: [] 0 added, 1 changed, 0 removed
reading sources... [100%] index
WARNING: autodoc: failed to import module 'run_me' from module 'main'; the following exception was raised:
No module named 'standalone'
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] index
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded, 1 warning.

The HTML pages are in build.

There are no HTML pages that refer to run_me.py in build. I have tried setting my root directory to all different kinds of directories and I have tried replacing all dots . with backslashes \ and so forth, but can't seem to find out what I'm doing wrong.

By the way, the statement that standalone is not a module is in fact true, it is just a directory without an __init__.py. Don't know if that might have caused some trouble?

Anyone have an idea?

mzjn
  • 48,958
  • 13
  • 128
  • 248
Sam
  • 113
  • 1
  • 1
  • 5
  • I think your intuition is correct. Add an empty __init__.py and see if the import warning goes away. – Steve Piercy Dec 07 '18 at 11:23
  • I tried it with the `__init__.py` added, but this does not resolve the error either. Do you know of anything else that may cause this error? – Sam Dec 07 '18 at 11:42
  • Without seeing your code, I assume that run_me.py tries to import standalone but fails. I'd need to see the code. – Steve Piercy Dec 07 '18 at 12:07
  • 1
    Sphinx will attempt to import all your code, just like running it in a Python interpreter. Fix your imports or specify the path to the non-importable code with sys in conf.py. – Steve Piercy Dec 08 '18 at 12:51
  • I finally fixed the `no module named` error. Indeed, the imports require that you define the root directory in `conf.py` to such a low level, that it is at least below the - in my case - `standalone` folder. I thus set my root directory to `picnic-data-shared-tools` and each time I call to a `.py` file, I move deeper into the project structure to access it. However, generating the `html` build files, I do not get an error this time, but just an empty html. Will try to find out what is happening here, otherwise, I'll be back. – Sam Dec 09 '18 at 16:57
  • 1
    Your module must be in a Python package that Sphinx can import. You need to put the module in a folder along with an empty `__init__.py` file (thus making it a Python package), and configure that path in your `conf.py` so Sphinx can import it. – Steve Piercy Jan 31 '20 at 04:52

5 Answers5

31

This is the usual "canonical approach" to "getting started" applied to the case when your source code resides in a src directory like Project/src instead of simply being inside the Project base directory.

Follows these steps:

  1. Create a docs directory in your Project directory (it's from this docs directory the commands in the following steps are executed).

  2. sphinx-quickstart (choose separate source from build. Places .html and .rst files in different folders).

  3. sphinx-apidoc -o ./source ../src

  4. make html

This would yield the following structure (provided you .py source files reside in Project/src):

Project
|
├───docs
│   │   make.bat
│   │   Makefile
│   │
│   ├───build
│   └───source
│       │   conf.py
│       │   index.rst
│       │   modules.rst
│       │   stack.rst
│       │
│       ├───_static
│       └───_templates
└───src
        stack.py

In your conf.py you'd add (after step 2):

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join('..', '..', 'src')))

Also include in conf.py:

extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']

And in index.rst you'd link modules.rst:

Welcome to Project's documentation!
================================

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

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

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

Your stack.rst and modules.rst were auto-generated by sphinx-apidoc, no need to change them (at this point). But just so you know this is what they look like:

stack.rst:

stack module
============

.. automodule:: stack
   :members:
   :undoc-members:
   :show-inheritance:

modules.rst:

src
===

.. toctree::
   :maxdepth: 4

   stack


After `make html` open `Project/docs/build/index.html` in your browser, the results:

enter image description here

and:

enter image description here

bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • 1
    At `conf.py` the command that works for me is `sys.path.insert(0, os.path.abspath(os.path.join('..', '..')))` otherwise I am getting the error `WARNING: autodoc: failed to import module 'source'; the following exception was raised: No module named 'source'`. also you are missing `__init__.py` file at `./src/` – Yogev Neumann Nov 25 '22 at 19:59
  • 1
    @YogevNeumann then you made a mistake because the example is correct. `__init__.py` was not included because `src` itself is meant to be a base directory, that's the recommended practice. – bad_coder Nov 25 '22 at 20:31
  • 1
    @YogevNeumann the mistake you are doing is putting an `__init__.py` inside `src`, that's not how a source layout is supposed to be used. You can put modules or packages inside `src` but it's not meant to be a package itself. That's the established practice that everyone uses, see [the docs](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/). – bad_coder Nov 25 '22 at 20:36
  • @bad_coder If i do that i get the error message `No module named 'src'` – Lupos Jan 07 '23 at 01:40
  • 1
    @Lupos the answer works exactly as it's shown, when something doesn't work it's because you changed something relative to the answer. Do you have a `src` directory? What's the complete error message? – bad_coder Jan 07 '23 at 02:00
  • @bad_coder thanks for the quick replay, I made it work by not only including 'src' but also the parent folder like yogev said, so including both. – Lupos Jan 07 '23 at 02:04
  • @Lupos if you've missed it I added a [link to the docs about the src layout](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/) in the comments. If you're using a `src` layout it's expected you know what it means in the context of the question, here's a simpler example [using a flat layout](https://stackoverflow.com/a/59951675). – bad_coder Jan 07 '23 at 02:42
  • this answer deserves a nobel prize – rictuar Jun 09 '23 at 00:41
6

Let's take an example with a project: dl4sci-school-2020 on master branch, commit: 6cbcc2c72d5dc74d2defa56bf63706fd628d9892:

├── dl4sci-school-2020
│   ├── LICENSE
│   ├── README.md
│   ├── src
│   │   └── __init__.py
│   └── utility
│       ├── __init__.py
│       └── utils.py

and utility package has a utils.py module:

Follow this process(FYI, I'm using sphinx-build 3.1.2):

  1. create a docs/ directory under you project:
mkdir docs
cd docs
  1. start sphinx within docs/, and just pass your project_name, your_name & version of your choice and rest keep defaults.
sphinx-quickstart

you will get below auto-generated in your docs/ folder

├── docs
│   ├── Makefile
│   ├── build
│   ├── make.bat
│   └── source
│       ├── _static
│       ├── _templates
│       ├── conf.py
│       └── index.rst

Since, we created a separate docs directory so we need sphinx find where to find build files and python src module. So, edit the conf.py file, you can use my conf.py file too

import os
import sys
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
sys.path.insert(0, basedir)

Now, to enable access to nested multiple packages & modules if any, you need to edit index.rst file.

.. toctree::
   :maxdepth: 2
   :caption: Description of my CodeBase:

   modules

The modules picks up content from modules.rst file which we will create below: Make sure you're still in doc/ to run the below command

sphinx-apidoc -o ./source ..

The output you get:

├── docs
│   ├── Makefile
│   ├── build
│   ├── make.bat
│   └── source
│       ├── _static
│       ├── _templates
│       ├── conf.py
│       ├── index.rst
│       ├── modules.rst
│       ├── src.rst
│       └── utility.rst

now run:

make html

Now, go and open in browser of your choice,

file:///<absolute_path_to_your_project>/dl4sci-school-2020/docs/build/html/index.html

have you beautiful documentation ready auto-generated python docs.

https://i.stack.imgur.com/5pvLu.jpg

FYI, You can switch any theme of your choice, I found sphinx_rtd_theme and extension sphinxcontrib.napoleon super dope!. Thanks to their creators, so I used it.

below does the work!

pip install sphinxcontrib-napoleon
pip install sphinx-rtd-theme

You can host your documentation it on readthedocs enjoy documenting your code!

  • 1
    **Warning to readers:** This answer is wrong and you'll me mislead if you follow it. The answer fails to use a `src` layout or a flat layout, see the docs [src layout vs flat layout](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/). If you want to use a `src` layout follow the top voted answer, if you want to use a flat layout see [this answer](https://stackoverflow.com/a/59951675). – bad_coder Nov 25 '22 at 21:08
3
sys.path.insert(0, os.path.abspath('../..'))

That's not correct. Steve Piercy's comment is not entirely on point (you don't need to add a __init__.py since you're using a simple module) but they're right that autodoc will try to import the module and then inspect the content.

Hoever assuming your tree is

doc/conf.py
src/stack.py

then you're just adding the folder which contains your repository to the sys.path which is completely useless. What you need to do is add the src folder to sys.path, such that when sphinx tries to import stack it finds your module. So your line should be:

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

(the path should be relative to conf.py).

Of note: since you have something which is completely synthetic and should contain no secrets, an accessible repository or a zip file of the entire thing makes it much easier to diagnose issues and provide relevant help: the less has to be inferred, the less can be wrong in the answer.

Masklinn
  • 34,759
  • 3
  • 38
  • 57
0

IMHO running pip install --no-deps -e . in the top project folder (or where ever setup.py is) to get an "editable" install is a better alternative to get your package modules on the PYTHONPATH than altering it in docs/conf.py using sys.path.

Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
-3

For me installing the package via setup.py file and re-running corresponding commands fixed the problem:

$ python setup.py install
Reza Behzadpour
  • 638
  • 5
  • 16