diff --git a/.github/workflows/docs-gh-pages.yml b/.github/workflows/docs-gh-pages.yml index 3953720..1734307 100644 --- a/.github/workflows/docs-gh-pages.yml +++ b/.github/workflows/docs-gh-pages.yml @@ -1,19 +1,38 @@ -name: Publish docs via GitHub Pages +name: Build and Deploy Documentation + on: push: branches: - main jobs: - build: - name: Deploy docs + build-and-deploy-docs: runs-on: ubuntu-latest steps: - - name: Checkout main - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for .git-restore-mtime to work correctly + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[docs] + + - name: Build MkDocs site (general documentation) + run: mkdocs build --config-file mkdocs.yml --site-dir ./site + + - name: Build Sphinx docs (api documentation) + run: | + sphinx-build -b html docs/api site/api - - name: Deploy docs - uses: mhausenblas/mkdocs-deploy-gh-pages@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REQUIREMENTS: docs/requirements.txt \ No newline at end of file + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site + force_orphan: true \ No newline at end of file diff --git a/docs/api/Makefile b/docs/api/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/api/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# 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) diff --git a/docs/api/conf.py b/docs/api/conf.py new file mode 100644 index 0000000..9950b9e --- /dev/null +++ b/docs/api/conf.py @@ -0,0 +1,77 @@ +import os + +# -- Path setup -------------------------------------------------------------- +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. +import sys +from pathlib import Path + +sys.path.insert(0, str(Path("..").resolve())) + +# -- Project information ----------------------------------------------------- +project = "mesa-frames" +author = "Adam Amer" +copyright = f"2023, {author}" + +# -- General configuration --------------------------------------------------- +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "numpydoc", + "sphinx_copybutton", + "sphinx_design", + "autodocsumm", +] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# -- Options for HTML output ------------------------------------------------- +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] +html_show_sourcelink = False + +# -- Extension settings ------------------------------------------------------ +# intersphinx mapping +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "pandas": ("https://pandas.pydata.org/docs/", None), + "polars": ("https://pola-rs.github.io/polars/py-polars/html/", None), +} + +# numpydoc settings +numpydoc_show_class_members = False + +# Copybutton settings +copybutton_prompt_text = r">>> |\.\.\. " +copybutton_prompt_is_regexp = True + +# -- Custom configurations --------------------------------------------------- +autoclass_content = "class" +autodoc_member_order = "bysource" +autodoc_default_options = {"special-members": True, "exclude-members": "__weakref__"} + +# -- GitHub link and user guide settings ------------------------------------- +github_root = "https://github.com/adamamer20/mesa-frames" +web_root = "https://adamamer20.github.io/mesa-frames" + +html_theme_options = { + "external_links": [ + { + "name": "User guide", + "url": f"{web_root}/user-guide/", + }, + ], + "icon_links": [ + { + "name": "GitHub", + "url": github_root, + "icon": "fa-brands fa-github", + }, + ], + "navbar_end": ["navbar-icon-links"], +} diff --git a/docs/api/index.rst b/docs/api/index.rst new file mode 100644 index 0000000..b8f090c --- /dev/null +++ b/docs/api/index.rst @@ -0,0 +1,27 @@ +mesa-frames API +=============== + +This page provides a high-level overview of all public mesa-frames objects, functions, and methods. All classes and functions exposed in the ``mesa_frames.*`` namespace are public. + +.. grid:: + + .. grid-item-card:: + + .. toctree:: + :maxdepth: 2 + + reference/agents/index + + .. grid-item-card:: + + .. toctree:: + :maxdepth: 1 + + reference/model + + .. grid-item-card:: + + .. toctree:: + :maxdepth: 3 + + reference/space/index \ No newline at end of file diff --git a/docs/api/make.bat b/docs/api/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/api/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/api/reference/agents/index.rst b/docs/api/reference/agents/index.rst new file mode 100644 index 0000000..0844eaa --- /dev/null +++ b/docs/api/reference/agents/index.rst @@ -0,0 +1,17 @@ +AgentSetDF +========== + +.. currentmodule:: mesa_frames + +.. autoclass:: AgentSetPandas + :members: + :inherited-members: + :autosummary: + :autosummary-nosignatures: + +.. autoclass:: AgentSetPolars + :members: + :inherited-members: + :autosummary: + :autosummary-nosignatures: + diff --git a/docs/api/reference/model.rst b/docs/api/reference/model.rst new file mode 100644 index 0000000..0cd115b --- /dev/null +++ b/docs/api/reference/model.rst @@ -0,0 +1,10 @@ +ModelDF +======= + +.. currentmodule:: mesa_frames + +.. autoclass:: ModelDF + :members: + :inherited-members: + :autosummary: + :autosummary-nosignatures: \ No newline at end of file diff --git a/docs/api/reference/space/grid/index.rst b/docs/api/reference/space/grid/index.rst new file mode 100644 index 0000000..fce661c --- /dev/null +++ b/docs/api/reference/space/grid/index.rst @@ -0,0 +1,16 @@ +GridDF +====== + +.. currentmodule:: mesa_frames + +.. autoclass:: GridPandas + :members: + :inherited-members: + :autosummary: + :autosummary-nosignatures: + +.. autoclass:: GridPolars + :members: + :inherited-members: + :autosummary: + :autosummary-nosignatures: \ No newline at end of file diff --git a/docs/api/reference/space/index.rst b/docs/api/reference/space/index.rst new file mode 100644 index 0000000..3e0ac40 --- /dev/null +++ b/docs/api/reference/space/index.rst @@ -0,0 +1,8 @@ +Space +===== +This page provides a high-level overview of possible space objects for mesa-frames models. + +.. toctree:: + :maxdepth: 2 + + grid/index \ No newline at end of file diff --git a/docs/index.md b/docs/general/index.md similarity index 100% rename from docs/index.md rename to docs/general/index.md diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index f8dbd67..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -mkdocs-jupyter -mkdocs-git-revision-date-localized-plugin -mkdocs-minify-plugin \ No newline at end of file diff --git a/mesa_frames/abstract/agents.py b/mesa_frames/abstract/agents.py index c9dbd22..1d4ac9c 100644 --- a/mesa_frames/abstract/agents.py +++ b/mesa_frames/abstract/agents.py @@ -68,21 +68,7 @@ def __init__(self, model): class AgentContainer(CopyMixin): - """An abstract class for containing agents. Defines the common interface for AgentSetDF and AgentsDF. - - Properties - ---------- - model : ModelDF - Get the model associated with the AgentContainer. - random : Generator - Get the random number generator associated with the model. - agents : DataFrame | dict[str, DataFrame] - Get or set the agents in the AgentContainer. - active_agents : DataFrame | dict[str, DataFrame] - Get or set the active agents in the AgentContainer. - inactive_agents : DataFrame | dict[str, DataFrame] - Get the inactive agents in the AgentContainer. - """ + """An abstract class for containing agents. Defines the common interface for AgentSetDF and AgentsDF.""" _copy_only_reference: list[str] = [ "_model", @@ -741,19 +727,6 @@ def pos(self) -> DataFrame | dict[str, DataFrame]: class AgentSetDF(AgentContainer, DataFrameMixin): """The AgentSetDF class is a container for agents of the same type. - Properties - ---------- - active_agents(self) -> DataFrame - Get the active agents in the AgentSetDF. - agents(self) -> DataFrame - Get or set the agents in the AgentSetDF. - inactive_agents(self) -> DataFrame - Get the inactive agents in the AgentSetDF. - model(self) -> ModelDF - Get the model associated with the AgentSetDF. - random(self) -> Generator - Get the random number generator associated with the model. - Parameters ---------- model : ModelDF diff --git a/mesa_frames/concrete/agents.py b/mesa_frames/concrete/agents.py index adf4752..87aa0d5 100644 --- a/mesa_frames/concrete/agents.py +++ b/mesa_frames/concrete/agents.py @@ -67,22 +67,7 @@ def step(self): class AgentsDF(AgentContainer): - """A collection of AgentSetDFs. All agents of the model are stored here. - - Properties - ---------- - active_agents(self) -> dict[AgentSetDF, pd.DataFrame] - Get the active agents in the AgentsDF. - agents(self) -> dict[AgentSetDF, pd.DataFrame] - Get or set the agents in the AgentsDF. - inactive_agents(self) -> dict[AgentSetDF, pd.DataFrame] - Get the inactive agents in the AgentsDF. - model(self) -> ModelDF - Get the model associated with the AgentsDF. - random(self) -> np.random.Generator - Get the random number generator associated with the model. - - """ + """A collection of AgentSetDFs. All agents of the model are stored here.""" _agentsets: list[AgentSetDF] _ids: pl.Series diff --git a/mesa_frames/concrete/model.py b/mesa_frames/concrete/model.py index bd09637..8e9bfa2 100644 --- a/mesa_frames/concrete/model.py +++ b/mesa_frames/concrete/model.py @@ -59,12 +59,6 @@ class ModelDF: It includes the basic attributes and methods necessary for initializing and running a simulation model. - Properties - ---------- - agents : AgentsDF - An AgentSet containing all agents in the model, generated from the _agents attribute. - agent_types : list of type - A list of different agent types present in the model. """ random: np.random.Generator diff --git a/mesa_frames/concrete/pandas/agentset.py b/mesa_frames/concrete/pandas/agentset.py index 758a8d2..66d12c0 100644 --- a/mesa_frames/concrete/pandas/agentset.py +++ b/mesa_frames/concrete/pandas/agentset.py @@ -62,12 +62,16 @@ def step(self): from mesa_frames.concrete.pandas.mixin import PandasMixin from mesa_frames.concrete.polars.agentset import AgentSetPolars from mesa_frames.types_ import AgentPandasMask, PandasIdsLike +from mesa_frames.utils import copydoc if TYPE_CHECKING: from mesa_frames.concrete.model import ModelDF -class AgentSetPandas(AgentSetDF, PandasMixin): # noqa : D101 +@copydoc(AgentSetDF) +class AgentSetPandas(AgentSetDF, PandasMixin): + """pandas-based implementation of AgentSetDF.""" + _agents: pd.DataFrame _mask: pd.Series _copy_with_method: dict[str, tuple[str, list[str]]] = { diff --git a/mesa_frames/concrete/pandas/mixin.py b/mesa_frames/concrete/pandas/mixin.py index a809325..b69c313 100644 --- a/mesa_frames/concrete/pandas/mixin.py +++ b/mesa_frames/concrete/pandas/mixin.py @@ -50,6 +50,8 @@ def _some_private_method(self): class PandasMixin(DataFrameMixin): + """pandas-based implementation of DataFrame operations.""" + def _df_add( self, df: pd.DataFrame, diff --git a/mesa_frames/concrete/pandas/space.py b/mesa_frames/concrete/pandas/space.py index 8889906..e513a87 100644 --- a/mesa_frames/concrete/pandas/space.py +++ b/mesa_frames/concrete/pandas/space.py @@ -61,9 +61,13 @@ def step(self): from mesa_frames.abstract.space import GridDF from mesa_frames.concrete.pandas.mixin import PandasMixin +from mesa_frames.utils import copydoc +@copydoc(GridDF) class GridPandas(GridDF, PandasMixin): + """pandas-based implementation of GridDF.""" + _agents: pd.DataFrame _copy_with_method: dict[str, tuple[str, list[str]]] = { "_agents": ("copy", ["deep"]), diff --git a/mesa_frames/concrete/polars/agentset.py b/mesa_frames/concrete/polars/agentset.py index e3d0fc3..9b7030d 100644 --- a/mesa_frames/concrete/polars/agentset.py +++ b/mesa_frames/concrete/polars/agentset.py @@ -67,13 +67,17 @@ def step(self): from mesa_frames.concrete.agents import AgentSetDF from mesa_frames.concrete.polars.mixin import PolarsMixin from mesa_frames.types_ import AgentPolarsMask, PolarsIdsLike +from mesa_frames.utils import copydoc if TYPE_CHECKING: from mesa_frames.concrete.model import ModelDF from mesa_frames.concrete.pandas.agentset import AgentSetPandas +@copydoc(AgentSetDF) class AgentSetPolars(AgentSetDF, PolarsMixin): + """Polars-based implementation of AgentSetDF.""" + _agents: pl.DataFrame _copy_with_method: dict[str, tuple[str, list[str]]] = { "_agents": ("clone", []), diff --git a/mesa_frames/concrete/polars/mixin.py b/mesa_frames/concrete/polars/mixin.py index 4736723..d9825da 100644 --- a/mesa_frames/concrete/polars/mixin.py +++ b/mesa_frames/concrete/polars/mixin.py @@ -55,6 +55,8 @@ def some_method(self): class PolarsMixin(DataFrameMixin): + """Polars-specific implementation of DataFrame operations.""" + # TODO: complete with other dtypes _dtypes_mapping: dict[str, Any] = {"int64": pl.Int64, "bool": pl.Boolean} diff --git a/mesa_frames/concrete/polars/space.py b/mesa_frames/concrete/polars/space.py index 27c9b90..04e20fe 100644 --- a/mesa_frames/concrete/polars/space.py +++ b/mesa_frames/concrete/polars/space.py @@ -50,9 +50,13 @@ def step(self): from mesa_frames.abstract.space import GridDF from mesa_frames.concrete.polars.mixin import PolarsMixin +from mesa_frames.utils import copydoc +@copydoc(GridDF) class GridPolars(GridDF, PolarsMixin): + """Polars-based implementation of GridDF.""" + _agents: pl.DataFrame _copy_with_method: dict[str, tuple[str, list[str]]] = { "_agents": ("clone", []), diff --git a/mesa_frames/utils.py b/mesa_frames/utils.py new file mode 100644 index 0000000..58b0c85 --- /dev/null +++ b/mesa_frames/utils.py @@ -0,0 +1,18 @@ +"""Utility functions for mesa_frames.""" + + +def copydoc(fromfunc, sep="\n"): + """Copy the docstring of function or class. + + https://stackoverflow.com/a/13743316 + """ + + def _decorator(func): + sourcedoc = fromfunc.__doc__ + if func.__doc__ == None: + func.__doc__ = sourcedoc + else: + func.__doc__ = sep.join([sourcedoc, func.__doc__]) + return func + + return _decorator diff --git a/mkdocs.yml b/mkdocs.yml index 0b9a3a6..0b21241 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,6 +3,7 @@ site_name: mesa-frames site_url: https://adamamer20.github.io/mesa-frames repo_url: https://github.com/adamamer20/mesa-frames repo_name: adamamer20/mesa-frames +docs_dir: docs/general # Theme configuration theme: diff --git a/pyproject.toml b/pyproject.toml index 26db853..5ff7a09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,13 +26,28 @@ polars = [ #"geopolars" (currently in pre-alpha) ] -docs = [ - "perfplot", #readme_script - "seaborn", #readme_script +mkdocs = [ "mkdocs-material", "mkdocs-jupyter", "mkdocs-git-revision-date-localized-plugin", - "mkdocs-minify-plugin" + "mkdocs-minify-plugin", +] + +sphinx = [ + "sphinx", + "sphinx-rtd-theme", + "numpydoc", + "pydata-sphinx-theme", + "sphinx-copybutton", + "sphinx-design", + "autodocsumm" +] + +docs = [ + "mesa_frames[pandas, polars, mkdocs, sphinx]", + # Readme Script + "perfplot", + "seaborn" ] dev = [