Skip to content

Commit

Permalink
Overhaul of documentation and tooling.
Browse files Browse the repository at this point in the history
  • Loading branch information
john-hen committed Nov 7, 2021
1 parent d31c593 commit 68e3828
Show file tree
Hide file tree
Showing 35 changed files with 372 additions and 327 deletions.
Binary file removed .coverage
Binary file not shown.
40 changes: 40 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test

on: [push, pull_request, workflow_dispatch]

jobs:

test:
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
runs-on: ubuntu-latest
steps:
- name: Check out code.
uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}.
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install package.
run: |
pip install --upgrade pip
pip install .[test,docs]
pip install flit
- name: Lint code.
run: pflake8

- name: Run tests.
run: pytest

- name: Build package.
run: python deploy/build.py

- name: Render docs.
run: python deploy/docs.py

- name: Clean repo.
run: python deploy/clean.py
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/develop/
/deploy/docs/
/deploy/dist/
/deploy/coverage/
/deploy/coverage.sqlite
/dist/
/deploy/coverage/
.coverage
**/__pycache__
**/.pytest_cache
flake8.txt
Expand Down
27 changes: 17 additions & 10 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@
Provides the fast, adaptive kernel density estimator based on linear
diffusion processes for one-dimensional and two-dimensional input data
as outlined in the [2010 paper by Botev et al.][paper] The reference
implementation for [1d][kde1d] and [2d][kde2d], in Matlab, was provided
by the paper's first author, Zdravko Botev. This is a re-implementation
in Python, with added test coverage.
implementation for [1d] and [2d], in Matlab, was provided by the paper's
first author, Zdravko Botev. This is a re-implementation in Python,
with added test coverage.

Find the full [documentation on Read-the-Docs][docs].

[paper]: https://dx.doi.org/10.1214/10-AOS799
[kde1d]: https://mathworks.com/matlabcentral/fileexchange/14034
[kde2d]: https://mathworks.com/matlabcentral/fileexchange/17204
[1d]: https://mathworks.com/matlabcentral/fileexchange/14034
[2d]: https://mathworks.com/matlabcentral/fileexchange/17204
[docs]: https://kde-diffusion.readthedocs.io

[![citation](https://zenodo.org/badge/263433787.svg)](https://zenodo.org/badge/latestdoi/263433787)
[![license](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![release](https://img.shields.io/pypi/v/kde-diffusion.svg)](https://pypi.python.org/pypi/kde-diffusion)
[![downloads](https://pepy.tech/badge/kde-diffusion)](https://pepy.tech/project/kde-diffusion)
[![release](
https://img.shields.io/pypi/v/kde-diffusion.svg?label=release)](
https://pypi.python.org/pypi/kde-diffusion)
[![downloads](
https://pepy.tech/badge/kde-diffusion)](
https://pepy.tech/project/kde-diffusion)
[![citation](
https://zenodo.org/badge/263433787.svg)](
https://zenodo.org/badge/latestdoi/263433787)
![coverage](tests/coverage.svg?raw=true)
[![documentation](https://readthedocs.org/projects/kde-diffusion/badge/?version=latest)](https://kde-diffusion.readthedocs.io/en/latest/?badge=latest)
[![documentation](
https://readthedocs.org/projects/kde-diffusion/badge/?version=latest)](
https://kde-diffusion.readthedocs.io/en/latest)
1 change: 0 additions & 1 deletion deploy/build.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Builds the installation package."""
__license__ = 'MIT'

from subprocess import run
from pathlib import Path
Expand Down
33 changes: 24 additions & 9 deletions deploy/clean.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
"""Deletes cache and build artifacts."""
__license__ = 'MIT'
"""Deletes build and test artifacts."""

from pathlib import Path
from shutil import rmtree

root = Path(__file__).resolve().parent.parent

for folder in root.rglob('__pycache__'):
rmtree(folder, ignore_errors=True)
folders = [
root/'deploy'/'docs',
root/'deploy'/'coverage',
root/'deploy'/'dist',
root/'dist',
]
folder_names = [
'__pycache__',
'.pytest_cache',
]
for folder_name in folder_names:
for folder in root.rglob(folder_name):
folders.append(folder)
for folder in folders:
if folder.is_dir():
rmtree(folder, ignore_errors=True)

for folder in root.rglob('.pytest_cache'):
rmtree(folder)

for folder in ('docs', 'dist', 'coverage'):
rmtree(root/'deploy'/folder, ignore_errors=True)
files = [
root/'.coverage',
root/'coverage.xml',
]
for file in files:
if file.is_file():
file.unlink()
18 changes: 6 additions & 12 deletions deploy/coverage.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
"""Measures code coverage by test suite."""
__license__ = 'MIT'


from subprocess import run
from pathlib import Path


here = Path(__file__).resolve().parent
root = here.parent
file = here/'coverage.sqlite'

run(['pytest', '--cov'], cwd=root)
print('Running test suite.')
run(['python', '-m', 'pytest', '--cov'], cwd=root)

print('Rendering coverage report.')
print('Exporting coverage report.')
folder = (here/'coverage').relative_to(root)
run(['coverage', 'html', f'--directory={folder}'], cwd=root)

badge = root/'tests'/file.with_suffix('.svg').name
if badge.exists():
print('Coverage badge already exists.')
else:
print('Rendering coverage badge.')
run(['coverage-badge', '-f', '-o', str(badge)], cwd=root)
print('Rendering coverage badge.')
badge = root/'tests'/'coverage.svg'
run(['coverage-badge', '-f', '-o', str(badge)], cwd=root)
1 change: 0 additions & 1 deletion deploy/render.py → deploy/docs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Renders the documentation."""
__license__ = 'MIT'

from subprocess import run
from pathlib import Path
Expand Down
1 change: 0 additions & 1 deletion deploy/publish.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Publishes the package on PyPI."""
__license__ = 'MIT'

from subprocess import run
from pathlib import Path
Expand Down
13 changes: 6 additions & 7 deletions deploy/release.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
Steps to take when releasing a new version:
* Bump version number and enter current date in `__init__.py`.
* Add a dedicated commit for the version bump.
* Tag the commit with the version number, for example: `git tag -a v1.0.3`.
* Enter the release notes as an annotation.
* Push the commit (but not the tag): `git push origin main`.
* Check that documentation built successfully on Read-the-Docs.
* Add dedicated commit for the version bump.
* Tag commit with version number, e.g. `git tag v1.0.3`.
* Push the commit: `git push origin main`.
* Check documentation build on Read-the-Docs.
* Publish on PyPI by running `deploy/publish.py`.
* Check that meta information is correct on PyPI.
* Then push the tag: `git push --tags`.
* Create a new release on GitHub and add the release notes.
* Push the tag: `git push --tags`.
* Create new release on GitHub and add release notes.
93 changes: 51 additions & 42 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
The documentation source comprises the `.md` files here, of which
`index.md` maps to the start page, as well as the doc-strings in the
package's source code for the API documentation. The Markdown parser
for `.md` files is MyST. For doc-strings it is CommonMark, which
for `.md` files is MyST. For doc-strings it is Commonmark, which
supports basic text formating, but no advanced features such as cross
references.
"""
__license__ = 'MIT'


########################################
Expand All @@ -28,76 +27,86 @@
from pathlib import Path # file-system path

extensions = [
'sphinx_rtd_theme', # Register Read-the-Docs theme.
'myst_parser', # Accept Markdown as input.
'sphinx.ext.autodoc', # Get documentation from doc-strings.
'sphinx.ext.autosummary', # Create summaries automatically.
'sphinx.ext.viewcode', # Add links to highlighted source code.
'sphinx.ext.mathjax', # Render math via JavaScript.
'sphinx.ext.viewcode', # Show source code.
'sphinx.ext.intersphinx', # Support short-hand web links.
]

# Add the project folder to the module search path.
main = Path(__file__).absolute().parent.parent
sys.path.insert(0, str(main))
root = Path(__file__).absolute().parent.parent
sys.path.insert(0, str(root))

# Mock external dependencies so they are not required at build time.
autodoc_mock_imports = ['numpy', 'scipy']
for package in ('numpy', 'scipy', 'scipy.fft', 'scipy.optimize'):
sys.modules[package] = MagicMock()

# Import package to make meta data available.
import kde_diffusion as meta


########################################
# Doc-strings #
########################################

def docstring(app, what, name, obj, options, lines):
"""Converts doc-strings from (CommonMark) Markdown to reStructuredText."""
md = '\n'.join(lines)
ast = commonmark.Parser().parse(md)
rst = commonmark.ReStructuredTextRenderer().render(ast)
lines.clear()
lines += rst.splitlines()


def setup(app):
"""Configure customized text processing."""
app.connect('autodoc-process-docstring', docstring)
# Make package meta data available.
from kde_diffusion import meta


########################################
# Configuration #
########################################

# Meta information
project = meta.__title__
version = meta.__version__
release = meta.__version__
date = meta.__date__
author = meta.__author__
copyright = meta.__copyright__
license = meta.__license__
project = meta.title
author = meta.author
copyright = meta.copyright
version = meta.version
release = version

# Web site
html_title = f'{project} {version}' # document title

# Source parsing
master_doc = 'index' # start page
nitpicky = True # Warn about missing references?

# Code documentation
add_module_names = False # Don't prefix members with module name.
autodoc_default_options = {
'members': True, # Include module/class members.
'member-order': 'bysource', # Order members as in source file.
}
autosummary_generate = False # Stub files are created by hand.
add_module_names = False # Don't prefix members with module name.

# Output style
html_theme = 'sphinx_rtd_theme' # Use the Read-the-Docs theme.
pygments_style = 'trac' # syntax highlighting style
html_static_path = ['style'] # folders to include in output
html_css_files = ['custom.css'] # extra style files to apply
# Short-hand web links
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'numpy': ('https://numpy.org/doc/stable', None),
'scipy': ('https://scipy.github.io/devdocs', None),
'sklearn': ('https://scikit-learn.org/stable', None),
'kdepy': ('https://kdepy.readthedocs.io/en/stable', None),
}

# Output options
# Rendering options
html_copy_source = False # Copy documentation source files?
html_show_copyright = False # Show copyright notice in footer?
html_show_sphinx = False # Show Sphinx blurb in footer?

# Rendering style
html_theme = 'furo' # Furo theme, with light and dark mode
pygments_style = 'friendly' # syntax highlight style in light mode
pygments_dark_style = 'stata-dark' # syntax highlight style in dark mode
html_static_path = ['style'] # folders to include in output
html_css_files = ['custom.css'] # extra style files to apply


########################################
# Doc-strings #
########################################

def docstring(app, what, name, obj, options, lines):
"""Converts doc-strings from (Commonmark) Markdown to reStructuredText."""
md = '\n'.join(lines)
ast = commonmark.Parser().parse(md)
rst = commonmark.ReStructuredTextRenderer().render(ast)
lines.clear()
lines += rst.splitlines()


def setup(app):
"""Sets up customized text processing."""
app.connect('autodoc-process-docstring', docstring)
22 changes: 8 additions & 14 deletions docs/implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ folder in the source-code [repository][repo] for details.
Subsequently, the Python code was refactored in order to blend in
better with the existing software ecosystem, namely by leveraging
SciPy's forward and backward discrete cosine transformation for one
or n dimensions, [`dct`][dct]/[`dctn`][dctn] and
[`idct`][idct]/[`idctn`][idctn], as well as NumPy's [`histogram`][hist]
and [`histogram2d`][hist2d], instead of the custom versions the Matlab
reference employs.
or n dimensions, [`dct`](scipy:scipy.fft.dct)/[`dctn`](scipy:scipy.fft.dctn)
and [`idct`](scipy:scipy.fft.idct)/[`idctn`](scipy:scipy.fft.idctn),
as well as NumPy's [`histogram`](numpy:numpy.histogram) and
[`histogram2d`](numpy:numpy.histogram2d), instead of the custom versions
the Matlab reference employs.

The reference uses a cosine transformation with a weight for the very
first component that is different from the one in any of the four types
Expand All @@ -41,7 +42,7 @@ estimations, such as SciPy's, as well as NumPy's 2d-histogram function.
When saving or displaying the 2d density as an image, a different
memory layout is expected and the index order has to be reversed: y
before x. This comes down to a simple transposition, i.e. adding
[`.T`][dotT] in the code.
[`.T`](numpy:numpy.ndarray.T) in the code.

In very broad strokes, the method is this:
* Bin the data on a regular grid.
Expand All @@ -57,12 +58,5 @@ In very broad strokes, the method is this:
[kde1d]: https://mathworks.com/matlabcentral/fileexchange/14034
[kde2d]: https://mathworks.com/matlabcentral/fileexchange/17204
[paper]: https://dx.doi.org/10.1214/10-AOS799
[verif]: https://github.com/John-Hennig/KDE-diffusion/tree/master/verification
[repo]: https://github.com/john-hennig/kde-diffusion
[dct]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.dct.html
[dctn]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.dctn.html
[idct]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.idct.html
[idctn]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.idctn.html
[hist]: https://numpy.org/doc/stable/reference/generated/numpy.histogram.html
[hist2d]: https://numpy.org/doc/stable/reference/generated/numpy.histogram2d.html
[dotT]: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.T.html
[verif]: https://github.com/john-hen/KDE-diffusion/tree/main/verification
[repo]: https://github.com/john-hen/kde-diffusion
Loading

0 comments on commit 68e3828

Please sign in to comment.