Skip to content

Commit

Permalink
Add doctest support (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
tachyonicClock authored Mar 6, 2024
1 parent 8bbc9bd commit 0ee1e30
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: docs
path: docs/_build/html
path: docs/_build
10 changes: 7 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ['sphinx.ext.autodoc', 'nbsphinx', 'sphinx.ext.mathjax', 'sphinx.ext.extlinks']
extensions = [
"sphinx.ext.autodoc",
"nbsphinx",
"sphinx.ext.mathjax",
"sphinx.ext.extlinks",
"sphinx.ext.doctest",
]

templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
Expand All @@ -27,7 +33,6 @@
}



# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

Expand All @@ -40,4 +45,3 @@
notebook_doc_source = Path("notebooks")
if not notebook_doc_source.exists():
os.symlink(notebooks, notebook_doc_source)

74 changes: 74 additions & 0 deletions docs/contributing.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,77 @@
Contributing
============

Adding Tests
------------

There are three ways to add tests to CapyMOA. Lots of these tests also serve as
documentation and examples.

PyTest
~~~~~~
Tests can be added to the ``tests`` directory. PyTest will automatically discover
and run these tests. They should be named ``test_*.py`` and the test functions
should be named ``test_*``. See the `PyTest documentation <https://docs.pytest.org>`_
for more information.

Use this type of test for parameterized tests, tests that require fixtures,
and tests that require a lot of setup.

These tests can be run with::
pytest

Or to run a specific test::

pytest tests/test_*.py

Or to run with the same configuration as continuous integration::

invoke test.unit

Doctest
~~~~~~~
Tests can be added to the docstrings of functions and classes using
`doctest <https://docs.python.org/3/library/doctest.html>`_. These tests
are written in the form of examples in a python interactive shell.

Use this type of test for simple examples and as a way to document the code.

CapyMOA uses the `sphinx.ext.doctest <https://www.sphinx-doc.org/en/master/usage/extensions/doctest.html#module-sphinx.ext.doctest>`_
to add these tests in the documentation. Here is an example of a doctest::

"""
.. doctest:: python

>>> print("Hello, World!")
Hello, World!
"""

They can be run with the following command::

pytest --doctest-modules

Or to run with the same configuration as continuous integration::

invoke test.unit


Notebooks
~~~~~~~~~
Tests can be added to the ``notebooks`` directory. These tests are written in the form of
`Jupyter Notebooks <https://jupyter.org>`_ and are run using the `nbmake`
extension for PyTest.

Use this type of test for tests that provide tutorials, documentation, and
examples. They should be easy to read and understand.

These test can be run with::

invoke test.nb

If you need to overwrite the notebooks with the output, use::

invoke test.nb --overwrite



2 changes: 2 additions & 0 deletions src/capymoa/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Hyper100k,
Sensor,
ElectricityTiny,
Fried
)

__all__ = [
Expand All @@ -16,4 +17,5 @@
"RTG_2abrupt",
"Sensor",
"ElectricityTiny",
"Fried"
]
7 changes: 7 additions & 0 deletions src/capymoa/datasets/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,10 @@ class CovtypeTiny(DownloadARFFGzip):

filename = "covtype_n1000.arff"
remote_url = ROOT_URL


class Fried(DownloadARFFGzip):
# TODO: Add docstring describing the dataset and link to the original source

filename = "fried.arff"
remote_url = ROOT_URL
38 changes: 37 additions & 1 deletion src/capymoa/stream/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Instance(ABC):
@property
@abstractmethod
def schema(self) -> "Schema":
"""Returns the schema of the instance and the stream it belongs to."""
"""Returns the schema of the instance and the stream it belongs to."""
...

@property
Expand All @@ -36,6 +36,26 @@ def java_instance(self) -> InstanceExample:


class LabeledInstance(Instance, ABC):
"""An :class:`Instance` with a class label.
.. doctest:: python
>>> from capymoa.datasets import ElectricityTiny
...
>>> from capymoa.stream.instance import LabeledInstance
>>> stream = ElectricityTiny()
>>> instance: LabeledInstance = stream.next_instance()
>>> instance.y_label
'1'
The label and index are NOT the same. One is a human-readable string
and the other is a integer representation of the class label.
>>> instance.y_index
1
>>> instance.x
array([0. , 0.056443, 0.439155, 0.003467, 0.422915, 0.414912])
"""

@property
@abstractmethod
def y_label(self) -> Label:
Expand All @@ -53,6 +73,22 @@ def y_index(self) -> LabelIndex:


class RegressionInstance(Instance, ABC):
"""An :class:`Instance` with a continuous target value.
.. doctest:: python
>>> from capymoa.datasets import Fried
...
>>> from capymoa.stream.instance import RegressionInstance
>>> stream = Fried()
>>> instance: RegressionInstance = stream.next_instance()
>>> instance.y_value
17.949
>>> instance.x
array([0.487, 0.072, 0.004, 0.833, 0.765, 0.6 , 0.132, 0.886, 0.073,
0.342])
"""

@property
@abstractmethod
def y_value(self) -> TargetValue:
Expand Down
22 changes: 18 additions & 4 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pathlib import Path
from typing import List
import wget
from os import cpu_count


def all_exist(files: List[str] = None, directories: List[str] = None) -> bool:
Expand All @@ -32,10 +33,11 @@ def all_exist(files: List[str] = None, directories: List[str] = None) -> bool:
@task()
def docs_build(ctx: Context):
"""Build the documentation using Sphinx."""
doc_dir = Path("docs/_build/html")
doc_dir = Path("docs/_build")
doc_dir.mkdir(exist_ok=True, parents=True)
cpu = cpu_count() // 2
print("Building documentation...")
ctx.run(f"python -m sphinx build -b html docs {doc_dir}")
ctx.run(f"python -m sphinx build -j {cpu} -E -b html docs {doc_dir}")

print("-" * 80)
print("Documentation is built and available at:")
Expand All @@ -44,6 +46,12 @@ def docs_build(ctx: Context):
print("-" * 80)


@task
def doc_test(ctx: Context):
"""Run the doctests."""
ctx.run("python -m sphinx -b doctest docs docs/_build")


@task
def docs_clean(ctx: Context):
"""Remove the built documentation."""
Expand All @@ -53,7 +61,7 @@ def docs_clean(ctx: Context):
@task
def docs_dev(ctx: Context):
"""Automatically rebuild the documentation when changes are detected."""
ctx.run("python -m sphinx_autobuild -b html docs docs/_build/html --open-browser")
ctx.run("python -m sphinx_autobuild -b html docs docs/_build --open-browser")


@task
Expand Down Expand Up @@ -143,7 +151,12 @@ def test_notebooks(ctx: Context, parallel: bool = True, overwrite: bool = False)
@task
def unittest(ctx: Context, parallel: bool = True):
"""Run the tests using pytest."""
cmd = ["python -m pytest", "--durations=0"] # Show the duration of each test
cmd = [
"python -m pytest",
"--doctest-modules", # Run tests defined in docstrings
"--durations=0", # Show the duration of each test
"-x", # Stop after the first failure
]
cmd += ["-n=auto"] if parallel else []
ctx.run(" ".join(cmd))

Expand All @@ -159,6 +172,7 @@ def all_tests(ctx: Context, parallel: bool = True):
docs.add_task(docs_build, "build")
docs.add_task(docs_clean, "clean")
docs.add_task(docs_dev, "dev")
docs.add_task(doc_test, "test")

build = Collection("build")
build.add_task(download_moa)
Expand Down

0 comments on commit 0ee1e30

Please sign in to comment.