From 9584216209c3a6bd80ebe46c2113c41c8352ee37 Mon Sep 17 00:00:00 2001 From: Sebastian Mohr <39738318+semohr@users.noreply.github.com> Date: Thu, 22 May 2025 11:35:40 +0200 Subject: [PATCH] Streamlined auto api referencing for documentation (#5795) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description The current developer documentation feels somewhat cluttered due to inline auto-generated API references for certain classes. To improve readability and maintainability, this PR introduces a more streamlined approach that aligns better with best practices observed in other PyData ecosystem documentation. Specifically, this PR: - Adds a dedicated `api/` folder to the documentation structure. - Moves all auto-generated references (classes, methods, etc.) to this folder. - Enables clean, concise linking to API elements from the narrative documentation—without interrupting human-written content with large autogenerated blocks. This separation makes the documentation easier to navigate and maintain, while still providing full API reference coverage where needed. - [x] Documentation - [x] Changelog --- docs/.gitignore | 2 + docs/Makefile | 3 +- docs/_templates/autosummary/base.rst | 3 + docs/_templates/autosummary/class.rst | 28 +++++ docs/_templates/autosummary/module.rst | 11 ++ docs/api/database.rst | 47 ++++++++ docs/api/plugins.rst | 11 ++ docs/changelog.rst | 5 + docs/conf.py | 46 ++++++-- docs/dev/index.rst | 10 ++ docs/dev/library.rst | 156 ++++++------------------- docs/dev/plugins.rst | 11 +- 12 files changed, 204 insertions(+), 129 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/_templates/autosummary/base.rst create mode 100644 docs/_templates/autosummary/class.rst create mode 100644 docs/_templates/autosummary/module.rst create mode 100644 docs/api/database.rst create mode 100644 docs/api/plugins.rst diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..1f041cc9d --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_build +generated/ \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile index f940dd931..d642530f1 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -6,6 +6,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build +SOURCEDIR = . # When both are available, use Sphinx 2.x for autodoc compatibility. ifeq ($(shell which sphinx-build2 >/dev/null 2>&1 ; echo $$?),0) @@ -39,7 +40,7 @@ help: @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: - -rm -rf $(BUILDDIR)/* + -rm -rf $(BUILDDIR)/* $(SOURCEDIR)/api/generated/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst new file mode 100644 index 000000000..822f55dc2 --- /dev/null +++ b/docs/_templates/autosummary/base.rst @@ -0,0 +1,3 @@ +{{ fullname | escape | underline}} +.. currentmodule:: {{ module }} +.. auto{{ objtype }}:: {{ objname }} \ No newline at end of file diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst new file mode 100644 index 000000000..6927f8360 --- /dev/null +++ b/docs/_templates/autosummary/class.rst @@ -0,0 +1,28 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: <-- add at least this line + :private-members: + :show-inheritance: <-- plus I want to show inheritance... + :inherited-members: <-- ...and inherited members too + + {% block methods %} + .. automethod:: __init__ + + {% if methods %} + .. rubric:: {{ _('Public methods summary') }} + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% for item in _methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + .. rubric:: {{ _('Methods definition') }} + diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst new file mode 100644 index 000000000..9383a2307 --- /dev/null +++ b/docs/_templates/autosummary/module.rst @@ -0,0 +1,11 @@ +{{ fullname | escape | underline}} +{% block modules %} +{% if modules %} +.. rubric:: Modules + +{% for item in modules %} +{{ item }} + +{%- endfor %} +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/docs/api/database.rst b/docs/api/database.rst new file mode 100644 index 000000000..627b5dc39 --- /dev/null +++ b/docs/api/database.rst @@ -0,0 +1,47 @@ +Database +-------- + +.. currentmodule:: beets.library + + +Library +''''''' + +.. autosummary:: + :toctree: generated/ + + Library + + +Models +'''''' + +.. autosummary:: + :toctree: generated/ + + LibModel + Album + Item + + +Transactions +'''''''''''' + +.. currentmodule:: beets.dbcore.db + +.. autosummary:: + :toctree: generated/ + + Transaction + +Queries +''''''' + +.. currentmodule:: beets.dbcore.query + +.. autosummary:: + :toctree: generated/ + + Query + FieldQuery + AndQuery \ No newline at end of file diff --git a/docs/api/plugins.rst b/docs/api/plugins.rst new file mode 100644 index 000000000..0d6c13718 --- /dev/null +++ b/docs/api/plugins.rst @@ -0,0 +1,11 @@ +Plugins +------- + +.. currentmodule:: beets.plugins + + + +.. autosummary:: + :toctree: generated/ + + BeetsPlugin diff --git a/docs/changelog.rst b/docs/changelog.rst index 44f2d305d..09259e1fa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -43,16 +43,21 @@ For plugin developers: Other changes: +* Documentation structure for auto generated API references changed slightly. + Autogenerated API references are now located in the `docs/api` subdirectory. + 2.3.1 (May 14, 2025) -------------------- Bug fixes: + * :doc:`/reference/pathformat`: Fixed a regression where path legalization incorrectly removed parts of user-configured path formats that followed a dot (**.**). :bug:`5771` For packagers: + * Force ``poetry`` version below 2 to avoid it mangling file modification times in ``sdist`` package. :bug:`5770` diff --git a/docs/conf.py b/docs/conf.py index 497c5e71e..d0f8cdffe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,19 +1,35 @@ -AUTHOR = "Adrian Sampson" +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html -# General configuration +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -extensions = ["sphinx.ext.autodoc", "sphinx.ext.extlinks"] - -exclude_patterns = ["_build"] -source_suffix = {".rst": "restructuredtext"} -master_doc = "index" project = "beets" +AUTHOR = "Adrian Sampson" copyright = "2016, Adrian Sampson" +master_doc = "index" +language = "en" version = "2.3" release = "2.3.1" +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.extlinks", +] +autosummary_generate = True +exclude_patterns = ["_build"] +templates_path = ["_templates"] +source_suffix = {".rst": "restructuredtext", ".md": "markdown"} + + pygments_style = "sphinx" # External links to the bug tracker and other sites. @@ -59,10 +75,24 @@ man_pages = [ ), ] -# Options for pydata theme + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + + html_theme = "pydata_sphinx_theme" html_theme_options = {"collapse_navigation": True, "logo": {"text": "beets"}} html_title = "beets" html_logo = "_static/beets_logo_nobg.png" html_static_path = ["_static"] html_css_files = ["beets.css"] + + +def skip_member(app, what, name, obj, skip, options): + if name.startswith("_"): + return True + return skip + + +def setup(app): + app.connect("autodoc-skip-member", skip_member) diff --git a/docs/dev/index.rst b/docs/dev/index.rst index 63335160c..10b3566c2 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -10,8 +10,18 @@ and write metadata tags in media files. .. _MediaFile: https://mediafile.readthedocs.io/en/latest/ .. toctree:: + :maxdepth: 1 plugins library importer cli + + +.. toctree:: + :maxdepth: 1 + :caption: API Reference + + ../api/plugins + ../api/database + diff --git a/docs/dev/library.rst b/docs/dev/library.rst index 9740c8b90..8c47e4dc3 100644 --- a/docs/dev/library.rst +++ b/docs/dev/library.rst @@ -20,30 +20,18 @@ invocation of beets usually has only one :class:`Library`. It's powered by abstraction, something like a very minimal `ORM`_. The library is also responsible for handling queries to retrieve stored objects. -.. autoclass:: Library(path, directory[, path_formats[, replacements]]) +Overview +'''''''' - .. automethod:: __init__ +You can add new items or albums to the library via the +:py:meth:`Library.add` and :py:meth:`Library.add_album` methods. - You can add new items or albums to the library: +You may also query the library for items and albums using the +:py:meth:`Library.items`, :py:meth:`Library.albums`, :py:meth:`Library.get_item` and :py:meth:`Library.get_album` methods. - .. automethod:: add - - .. automethod:: add_album - - And there are methods for querying the database: - - .. automethod:: items - - .. automethod:: albums - - .. automethod:: get_item - - .. automethod:: get_album - - Any modifications must go through a :class:`Transaction` which you get can - using this method: - - .. automethod:: transaction +Any modifications to the library must go through a +:class:`Transaction` object, which you can get using the +:py:meth:`Library.transaction` context manager. .. _SQLite: https://sqlite.org/index.html .. _ORM: https://en.wikipedia.org/wiki/Object-relational_mapping @@ -54,7 +42,7 @@ Model Classes The two model entities in beets libraries, :class:`Item` and :class:`Album`, share a base class, :class:`LibModel`, that provides common functionality. That -class itself specialises :class:`dbcore.Model` which provides an ORM-like +class itself specialises :class:`beets.dbcore.Model` which provides an ORM-like abstraction. To get or change the metadata of a model (an item or album), either access its @@ -68,42 +56,25 @@ Model base Models use dirty-flags to track when the object's metadata goes out of sync with the database. The dirty dictionary maps field names to booleans indicating whether the field has been written since the object was last -synchronized (via load or store) with the database. +synchronized (via load or store) with the database. This logic is implemented +in the model base class :class:`LibModel` and is inherited by both +:class:`Item` and :class:`Album`. -.. autoclass:: LibModel +We provide CRUD-like methods for interacting with the database: - .. automethod:: all_keys +* :py:meth:`LibModel.store` +* :py:meth:`LibModel.load` +* :py:meth:`LibModel.remove` +* :py:meth:`LibModel.add` - .. automethod:: __init__ +The base class :class:`beets.dbcore.Model` has a ``dict``-like interface, so +normal the normal mapping API is supported: - .. autoattribute:: _types +* :py:meth:`LibModel.keys` +* :py:meth:`LibModel.update` +* :py:meth:`LibModel.items` +* :py:meth:`LibModel.get` - .. autoattribute:: _fields - - There are CRUD-like methods for interacting with the database: - - .. automethod:: store - - .. automethod:: load - - .. automethod:: remove - - .. automethod:: add - - The base class :class:`dbcore.Model` has a ``dict``-like interface, so - normal the normal mapping API is supported: - - .. automethod:: keys - - .. automethod:: update - - .. automethod:: items - - .. note:: - The :py:meth:`Album.items` method is not inherited from - :py:meth:`LibModel.items` for historical reasons. - - .. automethod:: get Item '''' @@ -155,38 +126,6 @@ This leads to the following implementation policy: * On every modification to DB metadata (``item.field = ...``), the DB mtime is reset to zero. - -.. autoclass:: Item - - .. automethod:: __init__ - - .. automethod:: from_path - - .. automethod:: get_album - - .. automethod:: destination - - .. automethod:: current_mtime - - The methods ``read()`` and ``write()`` are complementary: one reads a - file's tags and updates the item's metadata fields accordingly while the - other takes the item's fields and writes them to the file's tags. - - .. automethod:: read - - .. automethod:: write - - .. automethod:: try_write - - .. automethod:: try_sync - - The :class:`Item` class supplements the normal model interface so that they - interacting with the filesystem as well: - - .. automethod:: move - - .. automethod:: remove - Album ''''' @@ -205,35 +144,10 @@ For those fields that are both item-level and album-level (e.g., ``year`` or use an SQLite table called ``albums``, in which each column is an album metadata field. -.. autoclass:: Album - .. automethod:: __init__ - - .. automethod:: item_dir - - .. automethod:: items - - Albums extend the normal model interface to also forward changes to their - items: - - .. autoattribute:: item_keys - - .. automethod:: store - - .. automethod:: try_sync - - .. automethod:: move - - .. automethod:: remove - - Albums also manage album art, image files that are associated with each - album: - - .. automethod:: set_art - - .. automethod:: move_art - - .. automethod:: art_destination +.. note:: + The :py:meth:`Album.items` method is not inherited from + :py:meth:`LibModel.items` for historical reasons. Transactions '''''''''''' @@ -241,24 +155,30 @@ Transactions The :class:`Library` class provides the basic methods necessary to access and manipulate its contents. To perform more complicated operations atomically, or to interact directly with the underlying SQLite database, you must use a -*transaction* (see this `blog post`_ for motivation). For example:: +*transaction* (see this `blog post`_ for motivation). For example + +.. code-block:: python lib = Library() with lib.transaction() as tx: items = lib.items(query) lib.add_album(list(items)) -.. _blog post: https://beets.io/blog/sqlite-nightmare.html - .. currentmodule:: beets.dbcore.db -.. autoclass:: Transaction - :members: +The :class:`Transaction` class is a context manager that provides a +transactional interface to the underlying SQLite database. It is +responsible for managing the transaction's lifecycle, including +beginning, committing, and rolling back the transaction if +an error occurs. +.. _blog post: https://beets.io/blog/sqlite-nightmare.html Queries ------- +.. currentmodule:: beets.dbcore.query + To access albums and items in a library, we use :doc:`/reference/query`. In beets, the :class:`Query` abstract base class represents a criterion that matches items or albums in the database. diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index 2d30f86c9..c24a94093 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -1,3 +1,11 @@ +Plugin Development Guide +======================== + +Beets plugins are Python modules or packages that extend the core functionality +of beets. The plugin system is designed to be flexible, allowing developers to +add virtually any type of features. + + .. _writing-plugins: Writing Plugins @@ -413,9 +421,8 @@ to extend the kinds of metadata that they can easily manage. The ``MediaFile`` class uses ``MediaField`` descriptors to provide access to file tags. If you have created a descriptor you can add it through -your plugins ``add_media_field()`` method. +your plugins :py:meth:`beets.plugins.BeetsPlugin.add_media_field()`` method. -.. automethod:: beets.plugins.BeetsPlugin.add_media_field .. _MediaFile: https://mediafile.readthedocs.io/en/latest/