From 3152bdaeb37c863a4e7942d8a243af1d7453c9ce Mon Sep 17 00:00:00 2001 From: Hannah Lazarus Date: Fri, 24 Apr 2020 10:23:41 -0400 Subject: [PATCH] Add sphinx defaults for cookiecutter'd project -serve, watch + live reload for docs + code file changes -update project makefile + make.bat -separate _source and _build -add packages and paths to use autodoc -edit/add documentation with examples (both at django-cookiecutter and inside generated project) -add formatted comments in User model for pickup by Sphinx apidoc -serve docs from a separate docs container for docker build --- CONTRIBUTORS.rst | 1 + docs/document.rst | 36 ++++---------- .../compose/local/docs/Dockerfile | 30 +++++++++++ {{cookiecutter.project_slug}}/docs/Makefile | 34 ++++++++++--- .../docs/_source/howto.rst | 47 ++++++++++++++++++ .../docs/{ => _source}/index.rst | 2 + .../{ => _source}/pycharm/configuration.rst | 0 .../docs/{ => _source}/pycharm/images/1.png | Bin .../docs/{ => _source}/pycharm/images/2.png | Bin .../docs/{ => _source}/pycharm/images/3.png | Bin .../docs/{ => _source}/pycharm/images/4.png | Bin .../docs/{ => _source}/pycharm/images/7.png | Bin .../docs/{ => _source}/pycharm/images/8.png | Bin .../docs/{ => _source}/pycharm/images/f1.png | Bin .../docs/{ => _source}/pycharm/images/f2.png | Bin .../docs/{ => _source}/pycharm/images/f3.png | Bin .../docs/{ => _source}/pycharm/images/f4.png | Bin .../{ => _source}/pycharm/images/issue1.png | Bin .../{ => _source}/pycharm/images/issue2.png | Bin .../docs/_source/users.rst | 15 ++++++ {{cookiecutter.project_slug}}/docs/conf.py | 26 ++++++---- {{cookiecutter.project_slug}}/docs/make.bat | 19 +++++-- {{cookiecutter.project_slug}}/local.yml | 16 ++++++ .../requirements/local.txt | 6 ++- .../users/models.py | 11 +++- 25 files changed, 193 insertions(+), 50 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/compose/local/docs/Dockerfile create mode 100644 {{cookiecutter.project_slug}}/docs/_source/howto.rst rename {{cookiecutter.project_slug}}/docs/{ => _source}/index.rst (96%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/configuration.rst (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/1.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/2.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/3.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/4.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/7.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/8.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/f1.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/f2.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/f3.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/f4.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/issue1.png (100%) rename {{cookiecutter.project_slug}}/docs/{ => _source}/pycharm/images/issue2.png (100%) create mode 100644 {{cookiecutter.project_slug}}/docs/_source/users.rst diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 86b529eb..660ffc68 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -128,6 +128,7 @@ Listed in alphabetical order. Guilherme Guy `@guilherme1guy`_ Hamish Durkin `@durkode`_ Hana Quadara `@hanaquadara`_ + Hannah Lazarus `@hanhanhan`_ Harry Moreno `@morenoh149`_ @morenoh149 Harry Percival `@hjwp`_ Hendrik Schneider `@hendrikschneider`_ diff --git a/docs/document.rst b/docs/document.rst index 7207e357..990bbe8a 100644 --- a/docs/document.rst +++ b/docs/document.rst @@ -4,42 +4,26 @@ Document ========= This project uses Sphinx_ documentation generator. -After you have set up to `develop locally`_, run the following commands to generate the HTML documentation: :: - $ sphinx-build docs/ docs/_build/html/ +After you have set up to `develop locally`_, run the following command from the project directory to build and serve HTML documentation: :: + + $ make -C docs livehtml If you set up your project to `develop locally with docker`_, run the following command: :: - $ docker-compose -f local.yml run --rm django sphinx-build docs/ docs/_build/html/ + $ docker-compose -f local.yml up docs + +Navigate to port 7000 on your host to see the documentation. This will be opened automatically at `localhost`_ for local, non-docker development. Generate API documentation ---------------------------- -Sphinx can automatically generate documentation from docstrings, to enable this feature, follow these steps: +Edit the docs/_source files and project application docstrings to create your documentation. -1. Add Sphinx extension in ``docs/conf.py`` file, like below: :: - - extensions = [ - 'sphinx.ext.autodoc', - ] - -2. Uncomment the following lines in the ``docs/conf.py`` file: :: - - # import django - # sys.path.insert(0, os.path.abspath('..')) - # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") - # django.setup() - -3. Run the following command: :: - - $ sphinx-apidoc -f -o ./docs/modules/ ./tpub/ migrations/* - - If you set up your project to `develop locally with docker`_, run the following command: :: - - $ docker-compose -f local.yml run --rm django sphinx-apidoc -f -o ./docs/modules ./tpub/ migrations/* - -4. Regenerate HTML documentation as written above. +Sphinx can automatically include class and function signatures and docstrings in generated documentation. +See the generated project documentation for more examples. +.. _localhost: http://localhost:7000/ .. _Sphinx: https://www.sphinx-doc.org/en/master/index.html .. _develop locally: ./developing-locally.html .. _develop locally with docker: ./developing-locally-docker.html diff --git a/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile new file mode 100644 index 00000000..7e79b0fa --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.8-slim-buster + +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update \ + # dependencies for building Python packages + && apt-get install -y build-essential \ + # psycopg2 dependencies + && apt-get install -y libpq-dev \ + # Translations dependencies + && apt-get install -y gettext \ + # Enable Sphinx output to latex and pdf + && apt-get install -y texlive-latex-recommended \ + && apt-get install -y texlive-fonts-recommended \ + && apt-get install -y texlive-latex-extra \ + && apt-get install -y latexmk \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements /requirements +# All imports needed for autodoc. +RUN pip install -r /requirements/local.txt +RUN pip install -r /requirements/production.txt + +WORKDIR /docs + +CMD make livehtml diff --git a/{{cookiecutter.project_slug}}/docs/Makefile b/{{cookiecutter.project_slug}}/docs/Makefile index d4bb2cbb..90f61de3 100644 --- a/{{cookiecutter.project_slug}}/docs/Makefile +++ b/{{cookiecutter.project_slug}}/docs/Makefile @@ -3,18 +3,38 @@ # You can set these variables from the command line, and also # from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build -c . +SOURCEDIR = ./_source +BUILDDIR = ./_build +{%- if cookiecutter.use_docker == 'y' %} +APP = /app +{%- else %} +APP = ../{{cookiecutter.project_slug}} +{% endif %} + +.PHONY: help livehtml apidocs Makefile # Put it first so that "make" without argument is like "make help". help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +# Build, watch and serve docs with live reload +livehtml: + sphinx-autobuild -b html + {%- if cookiecutter.use_docker == 'y' %} -H 0.0.0.0 + {%- else %} --open-browser + {%- endif %} -p 7000 --watch $(APP) -c . $(SOURCEDIR) $(BUILDDIR)/html + +# Outputs rst files from django application code +apidocs: + {%- if cookiecutter.use_docker == 'y' %} + sphinx-apidoc -o $(SOURCEDIR)/api /app + {%- else %} + sphinx-apidoc -o $(SOURCEDIR)/api ../{{cookiecutter.project_slug}} + {%- endif %} # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) -b $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/{{cookiecutter.project_slug}}/docs/_source/howto.rst b/{{cookiecutter.project_slug}}/docs/_source/howto.rst new file mode 100644 index 00000000..30927742 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/_source/howto.rst @@ -0,0 +1,47 @@ +How To - Project Documentation +====================================================================== + +Get Started +---------------------------------------------------------------------- + +Documentation can be written as rst files in the `{{cookiecutter.project_slug}}/docs/_source`. + +{% if cookiecutter.use_docker == 'n' %} +To build and serve docs, use the command: + :: + + make livehtml + +from inside the `{{cookiecutter.project_slug}}/docs` directory. +{% else %} +To build and serve docs, use the commands: + :: + + docker-compose -f local.yml build + docker run docs +{% endif %} + +Changes to files in `docs/_source` will be picked up and reloaded automatically. + +`Sphinx `_ is the tool used to build documentation. + +Docstrings to Documentation +---------------------------------------------------------------------- + +The sphinx extension `apidoc `_ is used to automatically document code using signatures and docstrings. + +Numpy or Google style docstrings will be picked up from project files and availble for documentation. See the `Napoleon `_ extension for details. + +For an in-use example, see the `page source <_sources/users.rst.txt>`_ for :ref:`users`. + +To compile all docstrings automatically into documentation source files, use the command: + :: + + make apidocs + +{% if cookiecutter.use_docker == 'y' %} +This can be done in the docker container: + :: + + docker run --rm docs make apidocs +{% endif -%} diff --git a/{{cookiecutter.project_slug}}/docs/index.rst b/{{cookiecutter.project_slug}}/docs/_source/index.rst similarity index 96% rename from {{cookiecutter.project_slug}}/docs/index.rst rename to {{cookiecutter.project_slug}}/docs/_source/index.rst index b107a9e5..5fafc696 100644 --- a/{{cookiecutter.project_slug}}/docs/index.rst +++ b/{{cookiecutter.project_slug}}/docs/_source/index.rst @@ -10,7 +10,9 @@ Welcome to {{ cookiecutter.project_name }}'s documentation! :maxdepth: 2 :caption: Contents: + howto pycharm/configuration + users diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst b/{{cookiecutter.project_slug}}/docs/_source/pycharm/configuration.rst similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/configuration.rst rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/configuration.rst diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/1.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/1.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/1.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/1.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/2.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/2.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/2.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/2.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/3.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/3.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/3.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/3.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/4.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/4.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/4.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/4.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/7.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/7.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/7.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/7.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/8.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/8.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/8.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/8.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/f1.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/f1.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/f1.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/f1.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/f2.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/f2.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/f2.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/f2.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/f3.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/f3.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/f3.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/f3.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/f4.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/f4.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/f4.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/f4.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/issue1.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/issue1.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/issue1.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/issue1.png diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/images/issue2.png b/{{cookiecutter.project_slug}}/docs/_source/pycharm/images/issue2.png similarity index 100% rename from {{cookiecutter.project_slug}}/docs/pycharm/images/issue2.png rename to {{cookiecutter.project_slug}}/docs/_source/pycharm/images/issue2.png diff --git a/{{cookiecutter.project_slug}}/docs/_source/users.rst b/{{cookiecutter.project_slug}}/docs/_source/users.rst new file mode 100644 index 00000000..6cf64556 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/_source/users.rst @@ -0,0 +1,15 @@ + .. _users: + +Users +====================================================================== + +Starting a new project, it’s highly recommended to set up a custom user model, +even if the default User model is sufficient for you. + +This model behaves identically to the default user model, +but you’ll be able to customize it in the future if the need arises. + +.. automodule:: {{cookiecutter.project_slug}}.users.models + :members: + :noindex: + diff --git a/{{cookiecutter.project_slug}}/docs/conf.py b/{{cookiecutter.project_slug}}/docs/conf.py index bfde1e3a..691f351e 100644 --- a/{{cookiecutter.project_slug}}/docs/conf.py +++ b/{{cookiecutter.project_slug}}/docs/conf.py @@ -9,15 +9,19 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -# + import os import sys +import django -# import django -# sys.path.insert(0, os.path.abspath('..')) -# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") -# django.setup() - +{% if cookiecutter.use_docker == 'y' %} +sys.path.insert(0, os.path.abspath("/app")) +os.environ.setdefault("DATABASE_URL", "") +{% else %} +sys.path.insert(0, os.path.abspath("..")) +{%- endif %} +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") +django.setup() # -- Project information ----------------------------------------------------- @@ -31,17 +35,19 @@ author = "{{ cookiecutter.author_name }}" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", +] # Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] +# templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -52,4 +58,4 @@ html_theme = "alabaster" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +# html_static_path = ["_static"] diff --git a/{{cookiecutter.project_slug}}/docs/make.bat b/{{cookiecutter.project_slug}}/docs/make.bat index 922152e9..b19f42c6 100644 --- a/{{cookiecutter.project_slug}}/docs/make.bat +++ b/{{cookiecutter.project_slug}}/docs/make.bat @@ -4,11 +4,13 @@ pushd %~dp0 REM Command file for Sphinx documentation + if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build + set SPHINXBUILD=sphinx-build -c . ) -set SOURCEDIR=. +set SOURCEDIR=_source set BUILDDIR=_build +set APP=..\{{cookiecutter.project_slug}} if "%1" == "" goto help @@ -20,16 +22,25 @@ if errorlevel 9009 ( echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. + echo.Install sphinx-autobuild for live serving. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +%SPHINXBUILD% -b %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end +:livehtml +sphinx-autobuild -b html --open-browser -p 7000 --watch %APP% -c . %SOURCEDIR% %BUILDDIR%/html +GOTO :EOF + +:apidocs +sphinx-apidoc -o %SOURCEDIR%/api %APP% +GOTO :EOF + :help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +%SPHINXBUILD% -b help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd diff --git a/{{cookiecutter.project_slug}}/local.yml b/{{cookiecutter.project_slug}}/local.yml index 60f8f922..0d9057b0 100644 --- a/{{cookiecutter.project_slug}}/local.yml +++ b/{{cookiecutter.project_slug}}/local.yml @@ -34,6 +34,22 @@ services: - local_postgres_data_backups:/backups env_file: - ./.envs/.local/.postgres + + docs: + image: {{ cookiecutter.project_slug }}_local_docs + container_name: docs + build: + context: . + dockerfile: ./compose/local/docs/Dockerfile + env_file: + - ./.envs/.local/.django + volumes: + - ./docs:/docs + - ./config:/app/config + - ./{{ cookiecutter.project_slug }}:/app/{{ cookiecutter.project_slug }} + ports: + - "7000:7000" + {%- if cookiecutter.use_mailhog == 'y' %} mailhog: diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index f6be44b5..253cf738 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -2,7 +2,6 @@ Werkzeug==1.0.1 # https://github.com/pallets/werkzeug ipdb==0.13.2 # https://github.com/gotcha/ipdb -Sphinx==3.0.2 # https://github.com/sphinx-doc/sphinx {%- if cookiecutter.use_docker == 'y' %} psycopg2==2.8.5 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- else %} @@ -16,6 +15,11 @@ django-stubs==1.5.0 # https://github.com/typeddjango/django-stubs pytest==5.3.5 # https://github.com/pytest-dev/pytest pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar +# Documentation +# ------------------------------------------------------------------------------ +sphinx==3.0.2 # https://github.com/sphinx-doc/sphinx +sphinx-autobuild # https://github.com/GaretJax/sphinx-autobuild + # Code quality # ------------------------------------------------------------------------------ flake8==3.7.9 # https://github.com/PyCQA/flake8 diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py index 8f07b15a..adff2d66 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py @@ -5,10 +5,17 @@ from django.utils.translation import ugettext_lazy as _ class User(AbstractUser): + """Default user for {{cookiecutter.project_name}}. + """ - # First Name and Last Name do not cover name patterns - # around the globe. + #: First and last name do not cover name patterns around the globe name = CharField(_("Name of User"), blank=True, max_length=255) def get_absolute_url(self): + """Get url for user's detail view. + + Returns: + str: URL for user detail. + + """ return reverse("users:detail", kwargs={"username": self.username})