From 0acc6c784794b8e2c0a345a989368feca22a99be Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Fri, 23 Jan 2026 15:31:56 +0100
Subject: [PATCH 01/15] Automatize API building
---
docs/css/custom.css | 15 +++-----
.../python/material/class.html.jinja | 37 +++++++++++++++++++
.../python/material/function.html.jinja | 37 +++++++++++++++++++
mkdocs.yml | 14 ++++++-
requirements-docs.txt | 3 +-
5 files changed, 94 insertions(+), 12 deletions(-)
diff --git a/docs/css/custom.css b/docs/css/custom.css
index c09c2be371..806330d39b 100644
--- a/docs/css/custom.css
+++ b/docs/css/custom.css
@@ -171,24 +171,21 @@
/*******************************************************/
/* Fix z-index. */
-header.md-header {
- z-index: 900 !important;
+.md-overlay {
+ z-index: 800 !important;
}
.md-sidebar {
- z-index: 1000 !important;
+ z-index: 900 !important;
}
-.md-overlay {
- z-index: 950 !important;
+header.md-header {
+ z-index: 1000 !important;
}
.md-search__overlay {
z-index: 1100 !important;
}
-.md-search__form {
+.md-search__inner {
z-index: 1200 !important;
}
-.md-search__output {
- z-index: 1100 !important;
-}
/*******************************************************/
/* Hide repo stats. */
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 40687fccf1..1ee1faa2bc 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -1,6 +1,24 @@
{% extends "_base/class.html.jinja" %}
{% block heading scoped %}
+ {% block alias_anchors scoped %}
+ {% if class.hopsworks_aliases and class.hopsworks_aliases.aliases %}
+ {% for alias in class.hopsworks_aliases.aliases -%}
+ {% for path in alias.paths -%}
+ {%- set alias_id = path + "." + (alias.as_alias or alias.object_name) -%}
+ {% filter heading(
+ heading_level,
+ role="class",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {%- endfor %}
+ {% endif %}
+ {% endblock alias_anchors %}
+
{% block source_link scoped %}
{% if config.extra.link_source and class.source_link %}
[source]
@@ -10,3 +28,22 @@
{{ super() }}
{% endblock heading %}
+
+{% block contents scoped %}
+ {% block aliases scoped %}
+ {% if class.hopsworks_aliases and class.hopsworks_aliases.aliases %}
+
+ Aliases: {% for alias in class.hopsworks_aliases.aliases -%}
+ {% for path in alias.paths -%}
+ {%- set fullpath = path + "." + (alias.as_alias or alias.object_name) -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}
+
+ {% endif %}
+ {% endblock aliases %}
+
+ {{ super() }}
+{% endblock contents %}
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index 7d621a25e3..2a41f81db1 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -1,6 +1,24 @@
{% extends "_base/function.html.jinja" %}
{% block heading scoped %}
+ {% block alias_anchors scoped %}
+ {% if function.hopsworks_aliases and function.hopsworks_aliases.aliases %}
+ {% for alias in function.hopsworks_aliases.aliases -%}
+ {% for path in alias.paths -%}
+ {%- set alias_id = path + "." + (alias.as_alias or alias.object_name) -%}
+ {% filter heading(
+ heading_level,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {%- endfor %}
+ {% endif %}
+ {% endblock alias_anchors %}
+
{% block source_link scoped %}
{% if config.extra.link_source and function.source_link %}
[source]
@@ -10,3 +28,22 @@
{{ super() }}
{% endblock heading %}
+
+{% block contents scoped %}
+ {% block aliases scoped %}
+ {% if function.hopsworks_aliases and function.hopsworks_aliases.aliases %}
+
+ Aliases: {% for alias in function.hopsworks_aliases.aliases -%}
+ {% for path in alias.paths -%}
+ {%- set fullpath = path + "." + (alias.as_alias or alias.object_name) -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}
+
+ {% endif %}
+ {% endblock aliases %}
+
+ {{ super() }}
+{% endblock contents %}
diff --git a/mkdocs.yml b/mkdocs.yml
index 474307ba1e..567c52edf7 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -264,7 +264,7 @@ nav:
- Access Audit Logs: setup_installation/admin/audit/audit-logs.md
- Export Audit Logs: setup_installation/admin/audit/export-audit-logs.md
- ArrowFlight Server with DuckDB: setup_installation/common/arrow_flight_duckdb.md
- - Python API: "!import https://github.com/logicalclocks/hopsworks-api?branch=main"
+ - Python API: python-api
- Java API: javadoc
- Community ↗: https://community.hopsworks.ai/
@@ -333,7 +333,15 @@ plugins:
minify_js: true
- mike:
canonical_version: latest
- - multirepo
+ - api-autonav:
+ modules:
+ - hopsworks
+ - hopsworks_common
+ - hsfs
+ - hsml
+ nav_section_title: Python API
+ api_root_uri: python-api
+ hide_empty: true
- mkdocstrings:
custom_templates: docs/templates
handlers:
@@ -351,6 +359,8 @@ plugins:
annotations_path: source
extra:
link_source: true
+ extensions:
+ - hopsworks_aliases.extension
inventories:
- https://docs.python.org/3/objects.inv
- https://pandas.pydata.org/docs/objects.inv
diff --git a/requirements-docs.txt b/requirements-docs.txt
index e3e38fea15..6ccf073ddd 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -4,7 +4,8 @@ mike==2.1.3
markdown==3.9
pymdown-extensions==10.17.2
mkdocs-minify-plugin>=0.2.0
-mkdocs-multirepo-plugin==0.8.3
+mkdocs-api-autonav@git+https://github.com/aversey/mkdocs-api-autonav.git
+hopsworks-aliases@git+https://github.com/aversey/hopsworks-aliases.git
mkdocstrings[python]==1.0.0
mkdocstrings-python==2.0.1
linkchecker
From 82b638b6ab7a4da6bb691b17d2ca5157e5a19091 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Tue, 27 Jan 2026 14:35:11 +0100
Subject: [PATCH 02/15] Show only public entities
---
mkdocs.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/mkdocs.yml b/mkdocs.yml
index 567c52edf7..50071e2510 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -357,6 +357,7 @@ plugins:
show_source: false
docstring_section_style: spacy
annotations_path: source
+ filters: public
extra:
link_source: true
extensions:
From f6edd029c37e62afc09bce682fcc61b05df4213b Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 4 Feb 2026 12:38:43 +0100
Subject: [PATCH 03/15] Show public aliases
---
.../python/material/class.html.jinja | 35 ++++++++-----------
.../python/material/function.html.jinja | 35 ++++++++-----------
mkdocs.yml | 8 ++---
requirements-docs.txt | 3 +-
4 files changed, 35 insertions(+), 46 deletions(-)
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 1ee1faa2bc..5820ed5a52 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -2,19 +2,17 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
- {% if class.hopsworks_aliases and class.hopsworks_aliases.aliases %}
- {% for alias in class.hopsworks_aliases.aliases -%}
- {% for path in alias.paths -%}
- {%- set alias_id = path + "." + (alias.as_alias or alias.object_name) -%}
- {% filter heading(
- heading_level,
- role="class",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {%- endfor %}
+ {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
+ {% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ heading_level+9,
+ role="class",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -31,14 +29,11 @@
{% block contents scoped %}
{% block aliases scoped %}
- {% if class.hopsworks_aliases and class.hopsworks_aliases.aliases %}
+ {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
- Aliases: {% for alias in class.hopsworks_aliases.aliases -%}
- {% for path in alias.paths -%}
- {%- set fullpath = path + "." + (alias.as_alias or alias.object_name) -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
- {%- endfor %}
+ Aliases: {% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
{%- if not loop.last %}, {% endif %}
{%- endfor %}
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index 2a41f81db1..e940070d21 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -2,19 +2,17 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
- {% if function.hopsworks_aliases and function.hopsworks_aliases.aliases %}
- {% for alias in function.hopsworks_aliases.aliases -%}
- {% for path in alias.paths -%}
- {%- set alias_id = path + "." + (alias.as_alias or alias.object_name) -%}
- {% filter heading(
- heading_level,
- role="function",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {%- endfor %}
+ {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
+ {% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ heading_level+9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -31,14 +29,11 @@
{% block contents scoped %}
{% block aliases scoped %}
- {% if function.hopsworks_aliases and function.hopsworks_aliases.aliases %}
+ {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
- Aliases: {% for alias in function.hopsworks_aliases.aliases -%}
- {% for path in alias.paths -%}
- {%- set fullpath = path + "." + (alias.as_alias or alias.object_name) -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
- {%- endfor %}
+ Aliases: {% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
{%- if not loop.last %}, {% endif %}
{%- endfor %}
diff --git a/mkdocs.yml b/mkdocs.yml
index 50071e2510..0758f36b67 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -333,15 +333,14 @@ plugins:
minify_js: true
- mike:
canonical_version: latest
- - api-autonav:
+ - hopsworks-apigen:
modules:
- - hopsworks
- hopsworks_common
- hsfs
- hsml
+ - hopsworks
nav_section_title: Python API
api_root_uri: python-api
- hide_empty: true
- mkdocstrings:
custom_templates: docs/templates
handlers:
@@ -354,6 +353,7 @@ plugins:
signature_crossrefs: true
show_symbol_type_heading: true
show_symbol_type_toc: true
+ members_order: source
show_source: false
docstring_section_style: spacy
annotations_path: source
@@ -361,7 +361,7 @@ plugins:
extra:
link_source: true
extensions:
- - hopsworks_aliases.extension
+ - hopsworks_apigen.mkdocs
inventories:
- https://docs.python.org/3/objects.inv
- https://pandas.pydata.org/docs/objects.inv
diff --git a/requirements-docs.txt b/requirements-docs.txt
index 6ccf073ddd..426d9e4724 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -4,8 +4,7 @@ mike==2.1.3
markdown==3.9
pymdown-extensions==10.17.2
mkdocs-minify-plugin>=0.2.0
-mkdocs-api-autonav@git+https://github.com/aversey/mkdocs-api-autonav.git
-hopsworks-aliases@git+https://github.com/aversey/hopsworks-aliases.git
+hopsworks-apigen@git+https://github.com/aversey/hopsworks-aliases.git
mkdocstrings[python]==1.0.0
mkdocstrings-python==2.0.1
linkchecker
From 9cf2c3bf0f053df716b755927c0c8b19e851d813 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 4 Feb 2026 12:40:58 +0100
Subject: [PATCH 04/15] Skip the primary alias
---
.../python/material/class.html.jinja | 28 +++++++++++--------
.../python/material/function.html.jinja | 28 +++++++++++--------
2 files changed, 32 insertions(+), 24 deletions(-)
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 5820ed5a52..4b4af7d639 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -4,15 +4,17 @@
{% block alias_anchors scoped %}
{% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
{% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- heading_level+9,
- role="class",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
+ {% if not loop.first %}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ heading_level+9,
+ role="class",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {% endif %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -32,9 +34,11 @@
{% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
Aliases: {% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
+ {% if not loop.first %}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {% endif %}
{%- endfor %}
{% endif %}
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index e940070d21..a202f09730 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -4,15 +4,17 @@
{% block alias_anchors scoped %}
{% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
{% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- heading_level+9,
- role="function",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
+ {% if not loop.first %}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ heading_level+9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {% endif %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -32,9 +34,11 @@
{% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
Aliases: {% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
+ {% if not loop.first %}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {% endif %}
{%- endfor %}
{% endif %}
From b98b683a0f144c9d8a494bbe8f99020adf4a12f6 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 4 Feb 2026 12:55:18 +0100
Subject: [PATCH 05/15] Fix TOC
---
.../python/material/class.html.jinja | 36 +++++++++----------
.../python/material/function.html.jinja | 36 +++++++++----------
2 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 4b4af7d639..17f6947347 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -3,18 +3,17 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
{% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
- {% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {% if not loop.first %}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- heading_level+9,
- role="class",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {% endif %}
+ {%- set public_aliases = class.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% for alias in public_aliases[1:] -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ 9,
+ role="class",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -32,15 +31,16 @@
{% block contents scoped %}
{% block aliases scoped %}
{% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
-
- Aliases: {% for alias in class.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {% if not loop.first %}
+ {%- set public_aliases = class.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% if public_aliases and public_aliases[1:] %}
+
+ Aliases: {% for alias in public_aliases[1:] -%}
{%- set fullpath = alias.target_module + "." + alias.alias_name -%}
{{fullpath}}
{%- if not loop.last %}, {% endif %}
- {% endif %}
- {%- endfor %}
-
+ {%- endfor %}
+
+ {% endif %}
{% endif %}
{% endblock aliases %}
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index a202f09730..0462ff6fa3 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -3,18 +3,17 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
{% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
- {% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {% if not loop.first %}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- heading_level+9,
- role="function",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {% endif %}
+ {%- set public_aliases = function.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% for alias in public_aliases[1:] -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ 9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
{%- endfor %}
{% endif %}
{% endblock alias_anchors %}
@@ -32,15 +31,16 @@
{% block contents scoped %}
{% block aliases scoped %}
{% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
-
- Aliases: {% for alias in function.extra.hopsworks_apigen.aliases if alias.is_public -%}
- {% if not loop.first %}
+ {%- set public_aliases = function.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% if public_aliases and public_aliases[1:] %}
+
+ Aliases: {% for alias in public_aliases[1:] -%}
{%- set fullpath = alias.target_module + "." + alias.alias_name -%}
{{fullpath}}
{%- if not loop.last %}, {% endif %}
- {% endif %}
- {%- endfor %}
-
+ {%- endfor %}
+
+ {% endif %}
{% endif %}
{% endblock aliases %}
From 55452914786b505126c6d422561c89e1fbc1befd Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 4 Feb 2026 13:09:28 +0100
Subject: [PATCH 06/15] Fix anchors
---
docs/templates/python/material/class.html.jinja | 3 +--
docs/templates/python/material/function.html.jinja | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 17f6947347..025bcfc1d3 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -3,8 +3,7 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
{% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
- {%- set public_aliases = class.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
- {% for alias in public_aliases[1:] -%}
+ {% for alias in class.extra.hopsworks_apigen.aliases -%}
{%- set alias_id = alias.target_module + "." + alias.alias_name -%}
{% filter heading(
9,
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index 0462ff6fa3..c6c494d542 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -3,8 +3,7 @@
{% block heading scoped %}
{% block alias_anchors scoped %}
{% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
- {%- set public_aliases = function.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
- {% for alias in public_aliases[1:] -%}
+ {% for alias in function.extra.hopsworks_apigen.aliases -%}
{%- set alias_id = alias.target_module + "." + alias.alias_name -%}
{% filter heading(
9,
From c282e617e47112ca56cdc1f0ba0df18a33a4dc8b Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Fri, 6 Feb 2026 15:31:53 +0100
Subject: [PATCH 07/15] Add ruff and respect aliases of methods too
---
docs/templates/python/material/function.html.jinja | 13 +++++++++++++
requirements-docs.txt | 1 +
2 files changed, 14 insertions(+)
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index c6c494d542..494f3e379f 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -15,6 +15,19 @@
) -%}{%- endfilter %}
{%- endfor %}
{% endif %}
+ {% if function.parent and function.parent.extra.hopsworks_apigen and function.parent.extra.hopsworks_apigen.aliases %}
+ {% for alias in function.parent.extra.hopsworks_apigen.aliases -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name + "." + function.name -%}
+ {% filter heading(
+ 9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {% endif %}
{% endblock alias_anchors %}
{% block source_link scoped %}
diff --git a/requirements-docs.txt b/requirements-docs.txt
index 426d9e4724..ad8d20e1e0 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -7,4 +7,5 @@ mkdocs-minify-plugin>=0.2.0
hopsworks-apigen@git+https://github.com/aversey/hopsworks-aliases.git
mkdocstrings[python]==1.0.0
mkdocstrings-python==2.0.1
+ruff==0.15.0
linkchecker
From df9aa9657888b4e90517efab1bdca0a1c015a22d Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Mon, 16 Feb 2026 11:31:48 +0100
Subject: [PATCH 08/15] Publish my version of API
---
.github/workflows/mkdocs-release.yml | 2 +-
.github/workflows/mkdocs-test.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/mkdocs-release.yml b/.github/workflows/mkdocs-release.yml
index 63ffc0f23f..4a751fc7d7 100644
--- a/.github/workflows/mkdocs-release.yml
+++ b/.github/workflows/mkdocs-release.yml
@@ -59,7 +59,7 @@ jobs:
- name: Checkout the API repo
uses: actions/checkout@v4
with:
- repository: logicalclocks/hopsworks-api
+ repository: aversey/hopsworks-api
ref: ${{ env.BRANCH }}
path: hopsworks-api
diff --git a/.github/workflows/mkdocs-test.yml b/.github/workflows/mkdocs-test.yml
index fa2c942f10..93a8527f61 100644
--- a/.github/workflows/mkdocs-test.yml
+++ b/.github/workflows/mkdocs-test.yml
@@ -15,7 +15,7 @@ jobs:
- name: Checkout the API repo
uses: actions/checkout@v4
with:
- repository: logicalclocks/hopsworks-api
+ repository: aversey/hopsworks-api
ref: ${{ github.base_ref }}
path: hopsworks-api
From 407f126e309379d0bb2733ace18ead55fe7e9a6a Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Tue, 17 Feb 2026 15:45:26 +0100
Subject: [PATCH 09/15] Add returned-by and hide empty attribute signatures
---
docs/css/custom.css | 12 +
.../python/material/attribute.html.jinja | 134 +++++-
.../python/material/backlinks.html.jinja | 25 ++
.../python/material/class.html.jinja | 383 ++++++++++++++++--
.../python/material/function.html.jinja | 261 +++++++++---
.../python/material/module.html.jinja | 135 +++++-
mkdocs.yml | 2 +
requirements-docs.txt | 7 +-
8 files changed, 849 insertions(+), 110 deletions(-)
create mode 100644 docs/templates/python/material/backlinks.html.jinja
diff --git a/docs/css/custom.css b/docs/css/custom.css
index 806330d39b..26f7a273cf 100644
--- a/docs/css/custom.css
+++ b/docs/css/custom.css
@@ -193,6 +193,18 @@ header.md-header {
display: none;
}
+/*******************************************************/
+/* Hide empty backlinks. */
+.info-backlinks:has(.doc-backlink-list:empty) {
+ display: none;
+}
+
+/*******************************************************/
+/* Hide symbol type labels from backlinks. */
+.doc-backlink-crumb .doc-symbol {
+ display: none;
+}
+
/*******************************************************/
/* Custom styles for syntax highlighting in signatures. */
diff --git a/docs/templates/python/material/attribute.html.jinja b/docs/templates/python/material/attribute.html.jinja
index 97eb8ebe34..f5d1fe083e 100644
--- a/docs/templates/python/material/attribute.html.jinja
+++ b/docs/templates/python/material/attribute.html.jinja
@@ -1,12 +1,130 @@
-{% extends "_base/attribute.html.jinja" %}
+{#- Template for Python attributes.
-{% block heading scoped %}
- {% block source_link scoped %}
- {% if config.extra.link_source and attribute.source_link %}
- [source]
+This template renders a Python attribute (or variable).
+This can be a module attribute or a class attribute.
+
+Context:
+ attribute (griffe.Attribute): The attribute to render.
+ root (bool): Whether this is the root object, injected with `:::` in a Markdown page.
+ heading_level (int): The HTML heading level to use.
+ config (dict): The configuration options.
+-#}
+
+{% block logs scoped %}
+ {#- Logging block.
+
+ This block can be used to log debug messages, deprecation messages, warnings, etc.
+ -#}
+ {{ log.debug("Rendering " + attribute.path) }}
+{% endblock logs %}
+
+
+ {% with obj = attribute, html_id = attribute.path %}
+
+ {% if root %}
+ {% set show_full_path = config.show_root_full_path %}
+ {% set root_members = True %}
+ {% elif root_members %}
+ {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
+ {% set root_members = False %}
+ {% else %}
+ {% set show_full_path = config.show_object_full_path %}
+ {% endif %}
+
+ {% set attribute_name = attribute.path if show_full_path else attribute.name %}
+
+ {% if not root or config.show_root_heading %}
+ {% filter heading(
+ heading_level,
+ role="data" if attribute.parent.kind.value == "module" else "attr",
+ id=html_id,
+ class="doc doc-heading",
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else attribute.name),
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+
+ {% block heading scoped %}
+ {#- Heading block.
+
+ This block renders the heading for the attribute.
+ -#}
+ {% block source_link scoped %}
+ {% if config.extra.link_source and attribute.source_link %}
+
[source]
+ {% endif %}
+ {% endblock source_link %}
+
+ {% if config.show_symbol_type_heading %}
{% endif %}
+ {% if config.heading and root %}
+ {{ config.heading }}
+ {% elif config.separate_signature %}
+
{{ attribute_name }}
+ {% else %}
+ {%+ filter highlight(language="python", inline=True) %}
+ {{ attribute_name }}{% if attribute.annotation and config.show_signature_annotations %}: {{ attribute.annotation }}{% endif %}
+ {% if config.show_attribute_values and attribute.value %} = {{ attribute.value }}{% endif %}
+ {% endfilter %}
+ {% endif %}
+ {% endblock heading %}
+
+ {% block labels scoped %}
+ {#- Labels block.
+
+ This block renders the labels for the attribute.
+ -#}
+ {% with labels = attribute.labels %}
+ {% include "labels.html.jinja" with context %}
+ {% endwith %}
+ {% endblock labels %}
+
+ {% endfilter %}
+
+ {% block signature scoped %}
+ {#- Signature block.
+
+ This block renders the signature for the attribute.
+ -#}
+ {% if config.separate_signature and (attribute.value or attribute.annotation) %}
+ {% filter format_attribute(attribute, config.line_length, crossrefs=config.signature_crossrefs, show_value=config.show_attribute_values) %}
+ {{ attribute.name }}
+ {% endfilter %}
+ {% endif %}
+ {% endblock signature %}
+
+ {% else %}
+
+ {% if config.show_root_toc_entry %}
+ {% filter heading(heading_level,
+ role="data" if attribute.parent.kind.value == "module" else "attr",
+ id=html_id,
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else attribute_name),
+ hidden=True,
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+ {% endfilter %}
+ {% endif %}
+ {% set heading_level = heading_level - 1 %}
{% endif %}
- {% endblock source_link %}
- {{ super() }}
+
+ {% block contents scoped %}
+ {#- Contents block.
+
+ This block renders the contents of the attribute.
+ It contains other blocks that users can override.
+ Overriding the contents block allows to rearrange the order of the blocks.
+ -#}
+ {% block docstring scoped %}
+ {#- Docstring block.
+
+ This block renders the docstring for the attribute.
+ -#}
+ {% with docstring_sections = attribute.docstring.parsed %}
+ {% include "docstring.html.jinja" with context %}
+ {% endwith %}
+ {% endblock docstring %}
+ {% endblock contents %}
+
-{% endblock heading %}
+ {% endwith %}
+
diff --git a/docs/templates/python/material/backlinks.html.jinja b/docs/templates/python/material/backlinks.html.jinja
new file mode 100644
index 0000000000..ecbfa2cac2
--- /dev/null
+++ b/docs/templates/python/material/backlinks.html.jinja
@@ -0,0 +1,25 @@
+{#- Template for backlinks.
+
+This template renders backlinks.
+
+Context:
+ backlinks (Mapping[str, Iterable[str]]): The backlinks to render.
+ config (dict): The configuration options.
+ verbose_type (Mapping[str, str]): The verbose backlink types.
+ default_crumb (BacklinkCrumb): A default, empty crumb.
+-#}
+
+{% macro render_crumb(crumb) %}
+
+ {% if crumb.url and crumb.title %}
+ {{ crumb.parent.title | replace(" ", "") | safe }}.{{ crumb.title | replace(" ", "") | safe }}
+ {% endif %}
+
+{% endmacro %}
+
+
+{%- for backlink in backlinks["returned-by"] | sort(attribute="crumbs") -%}
+
+ {{ render_crumb(backlink.crumbs[-1]) }}
+
+{%- endfor -%}
diff --git a/docs/templates/python/material/class.html.jinja b/docs/templates/python/material/class.html.jinja
index 025bcfc1d3..ed20289083 100644
--- a/docs/templates/python/material/class.html.jinja
+++ b/docs/templates/python/material/class.html.jinja
@@ -1,47 +1,350 @@
-{% extends "_base/class.html.jinja" %}
-
-{% block heading scoped %}
- {% block alias_anchors scoped %}
- {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
- {% for alias in class.extra.hopsworks_apigen.aliases -%}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- 9,
+{#- Template for Python classes.
+
+This template renders a Python class.
+
+Context:
+ class (griffe.Class): The class to render.
+ root (bool): Whether this is the root object, injected with `:::` in a Markdown page.
+ heading_level (int): The HTML heading level to use.
+ config (dict): The configuration options.
+-#}
+
+{% block logs scoped %}
+ {#- Logging block.
+
+ This block can be used to log debug messages, deprecation messages, warnings, etc.
+ -#}
+ {{ log.debug("Rendering " + class.path) }}
+{% endblock logs %}
+
+{% import "language.html.jinja" as lang with context %}
+{#- Language module providing the `t` translation method. -#}
+
+
+ {% with obj = class, html_id = class.path, all_members = class.all_members %}
+
+ {% if root %}
+ {% set show_full_path = config.show_root_full_path %}
+ {% set root_members = True %}
+ {% elif root_members %}
+ {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
+ {% set root_members = False %}
+ {% else %}
+ {% set show_full_path = config.show_object_full_path %}
+ {% endif %}
+
+ {% set class_name = class.path if show_full_path else class.name %}
+
+ {% if not root or config.show_root_heading %}
+ {% filter heading(
+ heading_level,
role="class",
- id=alias_id,
+ id=html_id,
class="doc doc-heading",
- hidden=true,
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else class.name),
skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {%- endfor %}
- {% endif %}
- {% endblock alias_anchors %}
+ ) %}
- {% block source_link scoped %}
- {% if config.extra.link_source and class.source_link %}
-
[source]
- {% endif %}
- {% endblock source_link %}
-
- {{ super() }}
-
-{% endblock heading %}
-
-{% block contents scoped %}
- {% block aliases scoped %}
- {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
- {%- set public_aliases = class.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
- {% if public_aliases and public_aliases[1:] %}
-
- Aliases: {% for alias in public_aliases[1:] -%}
- {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
- {%- endfor %}
-
+ {% block heading scoped %}
+ {#- Heading block.
+
+ This block renders the heading for the class.
+ -#}
+ {% block alias_anchors scoped %}
+ {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
+ {% for alias in class.extra.hopsworks_apigen.aliases -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ 9,
+ role="class",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {% endif %}
+ {% endblock alias_anchors %}
+
+ {% block source_link scoped %}
+ {% if config.extra.link_source and class.source_link %}
+
[source]
+ {% endif %}
+ {% endblock source_link %}
+
+ {% if config.show_symbol_type_heading %}
{% endif %}
+ {% if config.heading and root %}
+ {{ config.heading }}
+ {% elif config.separate_signature %}
+
{{ class_name }}
+ {% elif config.merge_init_into_class and "__init__" in all_members %}
+ {% with function = all_members["__init__"] %}
+ {%+ filter highlight(language="python", inline=True) %}
+ {{ class_name -}}
+ {%- with obj = function -%}
+ {%- include "type_parameters.html.jinja" with context -%}
+ {%- endwith -%}
+ {%- include "signature.html.jinja" with context -%}
+ {% endfilter %}
+ {% endwith %}
+ {% else %}
+ {# TODO: Maybe render type parameters here. #}
+
{{ class_name }}
+ {% endif %}
+ {% endblock heading %}
+
+ {% block labels scoped %}
+ {#- Labels block.
+
+ This block renders the labels for the class.
+ -#}
+ {% with labels = class.labels %}
+ {% include "labels.html.jinja" with context %}
+ {% endwith %}
+ {% endblock labels %}
+
+ {% endfilter %}
+
+ {% block signature scoped %}
+ {#- Signature block.
+
+ This block renders the signature for the class.
+ Overloads of the `__init__` method are rendered if `merge_init_into_class` is enabled.
+ The actual `__init__` method signature is only rendered if `separate_signature` is also enabled.
+
+ If the class is generic, but the `__init__` method isn't or `merge_init_into_class` is disabled,
+ the class signature is rendered if `separate_signature` and `show_signature_type_parameters` are enabled.
+
+ If the `__init__` method or any overloads are generic, they are rendered as methods if
+ `merge_init_into_class`, `separate_signature` and `show_signature_type_parameters` are enabled.
+ -#}
+ {% if config.merge_init_into_class and "__init__" in all_members %}
+ {% with function = all_members["__init__"] %}
+ {% if function.overloads and config.show_overloads %}
+
+ {% for overload in function.overloads %}
+ {% filter format_signature(overload, config.line_length, annotations=True, crossrefs=config.signature_crossrefs) %}
+ {{ class.name }}
+ {% endfilter %}
+ {% endfor %}
+
+ {% endif %}
+ {% if config.separate_signature and not (config.show_overloads and function.overloads and config.overloads_only) %}
+ {% filter format_signature(function, config.line_length, crossrefs=config.signature_crossrefs) %}
+ {{ class.name }}
+ {% endfilter %}
+ {% endif %}
+ {% endwith %}
+ {% endif %}
+ {% endblock signature %}
+
+ {% else %}
+ {% if config.show_root_toc_entry %}
+ {% filter heading(heading_level,
+ role="class",
+ id=html_id,
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else class.name),
+ hidden=True,
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+ {% endfilter %}
{% endif %}
+ {% set heading_level = heading_level - 1 %}
{% endif %}
- {% endblock aliases %}
- {{ super() }}
-{% endblock contents %}
+
+ {% block contents scoped %}
+ {#- Contents block.
+
+ This block renders the contents of the class.
+ It contains other blocks that users can override.
+ Overriding the contents block allows to rearrange the order of the blocks.
+ -#}
+ {% block bases scoped %}
+ {#- Class bases block.
+
+ This block renders the bases for the class.
+ -#}
+ {% if config.show_bases and class.bases %}
+
+ Bases: {% for expression in class.bases -%}
+
+ {%- with backlink_type = "subclassed-by" -%}
+ {%- include "expression.html.jinja" with context -%}
+ {%- endwith -%}
+ {% if not loop.last %}, {% endif %}
+ {% endfor -%}
+
+ {% endif %}
+ {% endblock bases %}
+
+ {% block inheritance_diagram scoped %}
+ {#- Inheritance diagram block.
+
+ This block renders the inheritance diagram for the class,
+ using Mermaid syntax and a bit of JavaScript to make the nodes clickable,
+ linking to the corresponding class documentation.
+ -#}
+ {% if config.show_inheritance_diagram and class.bases %}
+ {% macro edges(class) %}
+ {% for base in class.resolved_bases %}
+ {{ base.path }} --> {{ class.path }}
+ {{ edges(base) }}
+ {% endfor %}
+ {% endmacro %}
+
+
+ {% for base in class.mro() %}
+
+ {% endfor %}
+
+
+ flowchart {{ config.inheritance_diagram_direction }}
+ {{ class.path }}[{{ class.name }}]
+ {% for base in class.mro() %}
+ {{ base.path }}[{{ base.name }}]
+ {% endfor %}
+
+ {{ edges(class) | safe }}
+
+ click {{ class.path }} href "" "{{ class.path }}"
+ {% for base in class.mro() %}
+ click {{ base.path }} href "" "{{ base.path }}"
+ {% endfor %}
+
+
+ {% endif %}
+ {% endblock inheritance_diagram %}
+
+ {% block docstring scoped %}
+ {#- Docstring block.
+
+ This block renders the docstring for the class.
+ -#}
+ {% with docstring_sections = class.docstring.parsed %}
+ {% include "docstring.html.jinja" with context %}
+ {% endwith %}
+ {% if config.merge_init_into_class %}
+ {# We don't want to merge the inherited `__init__` method docstring into the class docstring #}
+ {# if such inherited method was not selected through `inherited_members`. #}
+ {% with check_members = all_members if (config.inherited_members is true or (config.inherited_members is iterable and "__init__" in config.inherited_members)) else class.members %}
+ {% if "__init__" in check_members and check_members["__init__"].has_docstring %}
+ {% with function = check_members["__init__"] %}
+ {% with obj = function, docstring_sections = function.docstring.parsed %}
+ {% include "docstring.html.jinja" with context %}
+ {% endwith %}
+ {% endwith %}
+ {% endif %}
+ {% endwith %}
+ {% endif %}
+ {% endblock docstring %}
+
+ {% block aliases scoped %}
+ {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
+ {%- set public_aliases = class.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% if public_aliases and public_aliases[1:] %}
+
+ For backwards compatibility
+ {% set fullpath = public_aliases[0].target_module + "." + public_aliases[0].alias_name %}
+ {{fullpath}}
+ is still available as
+ {% for alias in public_aliases[1:] -%}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}.
+ {% if public_aliases[2:] %}
+ The use of these aliases is discouraged as they are to be deprecated.
+ {% else %}
+ The use of this alias is discouraged as it is to be deprecated.
+ {% endif %}
+
+ {% endif %}
+ {% endif %}
+ {% endblock aliases %}
+
+ {% block backlinks scoped %}
+ {% if class.extra.hopsworks_apigen and class.extra.hopsworks_apigen.aliases %}
+
+ Returned by
+
+
+ {%- for alias in class.extra.hopsworks_apigen.aliases -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% if alias_id != class.path %}
+
+ {% endif %}
+ {%- endfor -%}
+
+
+ {% endif %}
+ {% endblock backlinks %}
+
+ {% block summary scoped %}
+ {#- Summary block.
+
+ This block renders auto-summaries for classes, methods, and attributes.
+ -#}
+ {% include "summary.html.jinja" with context %}
+ {% endblock summary %}
+
+ {% block source scoped %}
+ {#- Source block.
+
+ This block renders the source code for the class.
+ -#}
+ {% if config.show_source %}
+ {% if config.merge_init_into_class %}
+ {% if "__init__" in all_members and all_members["__init__"].source %}
+ {% with init = all_members["__init__"] %}
+
+ {{ lang.t("Source code in") }}
+ {%- if init.relative_filepath.is_absolute() -%}
+ {{ init.relative_package_filepath }}
+ {%- else -%}
+ {{ init.relative_filepath }}
+ {%- endif -%}
+
+ {{ init.source|highlight(language="python", linestart=init.lineno or 0, linenums=True) }}
+
+ {% endwith %}
+ {% endif %}
+ {% elif class.source %}
+
+ {{ lang.t("Source code in") }}
+ {%- if class.relative_filepath.is_absolute() -%}
+ {{ class.relative_package_filepath }}
+ {%- else -%}
+ {{ class.relative_filepath }}
+ {%- endif -%}
+
+ {{ class.source|highlight(language="python", linestart=class.lineno or 0, linenums=True) }}
+
+ {% endif %}
+ {% endif %}
+ {% endblock source %}
+
+ {% block children scoped %}
+ {#- Children block.
+
+ This block renders the children (members) of the class.
+ -#}
+ {% set root = False %}
+ {% set heading_level = heading_level + 1 %}
+ {% include "children.html.jinja" with context %}
+ {% endblock children %}
+ {% endblock contents %}
+
+
+ {% endwith %}
+
diff --git a/docs/templates/python/material/function.html.jinja b/docs/templates/python/material/function.html.jinja
index 494f3e379f..b2f67aee97 100644
--- a/docs/templates/python/material/function.html.jinja
+++ b/docs/templates/python/material/function.html.jinja
@@ -1,60 +1,219 @@
-{% extends "_base/function.html.jinja" %}
+{#- Template for Python functions.
-{% block heading scoped %}
- {% block alias_anchors scoped %}
- {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
- {% for alias in function.extra.hopsworks_apigen.aliases -%}
- {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
- {% filter heading(
- 9,
- role="function",
- id=alias_id,
- class="doc doc-heading",
- hidden=true,
- skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {%- endfor %}
+This template renders a Python function or method.
+
+Context:
+ function (griffe.Function): The function to render.
+ root (bool): Whether this is the root object, injected with `:::` in a Markdown page.
+ heading_level (int): The HTML heading level to use.
+ config (dict): The configuration options.
+-#}
+
+{% block logs scoped %}
+ {#- Logging block.
+
+ This block can be used to log debug messages, deprecation messages, warnings, etc.
+ -#}
+ {{ log.debug("Rendering " + function.path) }}
+{% endblock logs %}
+
+{% import "language.html.jinja" as lang with context %}
+{#- Language module providing the `t` translation method. -#}
+
+
+ {% with obj = function, html_id = function.path %}
+
+ {% if root %}
+ {% set show_full_path = config.show_root_full_path %}
+ {% set root_members = True %}
+ {% elif root_members %}
+ {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
+ {% set root_members = False %}
+ {% else %}
+ {% set show_full_path = config.show_object_full_path %}
{% endif %}
- {% if function.parent and function.parent.extra.hopsworks_apigen and function.parent.extra.hopsworks_apigen.aliases %}
- {% for alias in function.parent.extra.hopsworks_apigen.aliases -%}
- {%- set alias_id = alias.target_module + "." + alias.alias_name + "." + function.name -%}
- {% filter heading(
- 9,
+
+ {% set function_name = function.path if show_full_path else function.name %}
+ {#- Brief or full function name depending on configuration. -#}
+ {% set symbol_type = "method" if function.parent.is_class else "function" %}
+ {#- Symbol type: method when parent is a class, function otherwise. -#}
+
+ {% if not root or config.show_root_heading %}
+ {% filter heading(
+ heading_level,
role="function",
- id=alias_id,
+ id=html_id,
class="doc doc-heading",
- hidden=true,
+ toc_label=(('
')|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else function.name),
skip_inventory=config.skip_local_inventory,
- ) -%}{%- endfilter %}
- {%- endfor %}
- {% endif %}
- {% endblock alias_anchors %}
+ ) %}
- {% block source_link scoped %}
- {% if config.extra.link_source and function.source_link %}
-
[source]
- {% endif %}
- {% endblock source_link %}
-
- {{ super() }}
-
-{% endblock heading %}
-
-{% block contents scoped %}
- {% block aliases scoped %}
- {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
- {%- set public_aliases = function.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
- {% if public_aliases and public_aliases[1:] %}
-
- Aliases: {% for alias in public_aliases[1:] -%}
- {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
- {{fullpath}}
- {%- if not loop.last %}, {% endif %}
- {%- endfor %}
-
+ {% block heading scoped %}
+ {#- Heading block.
+
+ This block renders the heading for the function.
+ -#}
+ {% block alias_anchors scoped %}
+ {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
+ {% for alias in function.extra.hopsworks_apigen.aliases -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name -%}
+ {% filter heading(
+ 9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {% endif %}
+ {% if function.parent and function.parent.extra.hopsworks_apigen and function.parent.extra.hopsworks_apigen.aliases %}
+ {% for alias in function.parent.extra.hopsworks_apigen.aliases -%}
+ {%- set alias_id = alias.target_module + "." + alias.alias_name + "." + function.name -%}
+ {% filter heading(
+ 9,
+ role="function",
+ id=alias_id,
+ class="doc doc-heading",
+ hidden=true,
+ skip_inventory=config.skip_local_inventory,
+ ) -%}{%- endfilter %}
+ {%- endfor %}
+ {% endif %}
+ {% endblock alias_anchors %}
+
+ {% block source_link scoped %}
+ {% if config.extra.link_source and function.source_link %}
+
[source]
+ {% endif %}
+ {% endblock source_link %}
+
+ {% if config.show_symbol_type_heading %}
{% endif %}
+ {% if config.heading and root %}
+ {{ config.heading }}
+ {% elif config.separate_signature %}
+
{{ function_name }}
+ {% else %}
+ {%+ filter highlight(language="python", inline=True) -%}
+ {{ function_name }}
+ {%- include "type_parameters.html.jinja" with context -%}
+ {%- include "signature.html.jinja" with context -%}
+ {%- endfilter %}
+ {% endif %}
+ {% endblock heading %}
+
+ {% block labels scoped %}
+ {#- Labels block.
+
+ This block renders the labels for the function.
+ -#}
+ {% with labels = function.labels %}
+ {% include "labels.html.jinja" with context %}
+ {% endwith %}
+ {% endblock labels %}
+
+ {% endfilter %}
+
+ {% block signature scoped %}
+ {#- Signature block.
+
+ This block renders the signature for the function,
+ as well as its overloaded signatures if any.
+ -#}
+ {% if function.overloads and config.show_overloads %}
+
+ {% for overload in function.overloads %}
+ {% filter format_signature(overload, config.line_length, annotations=True, crossrefs=config.signature_crossrefs) %}
+ {{ overload.name }}
+ {% endfilter %}
+ {% endfor %}
+
+ {% endif %}
+ {% if config.separate_signature and not (config.show_overloads and function.overloads and config.overloads_only) %}
+ {% filter format_signature(function, config.line_length, crossrefs=config.signature_crossrefs) %}
+ {{ function.name }}
+ {% endfilter %}
+ {% endif %}
+ {% endblock signature %}
+
+ {% else %}
+
+ {% if config.show_root_toc_entry %}
+ {% filter heading(
+ heading_level,
+ role="function",
+ id=html_id,
+ toc_label=(('
')|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else function.name),
+ hidden=True,
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+ {% endfilter %}
{% endif %}
+ {% set heading_level = heading_level - 1 %}
{% endif %}
- {% endblock aliases %}
- {{ super() }}
-{% endblock contents %}
+
+ {% block contents scoped %}
+ {#- Contents block.
+
+ This block renders the contents of the function.
+ It contains other blocks that users can override.
+ Overriding the contents block allows to rearrange the order of the blocks.
+ -#}
+ {% block aliases scoped %}
+ {% if function.extra.hopsworks_apigen and function.extra.hopsworks_apigen.aliases %}
+ {%- set public_aliases = function.extra.hopsworks_apigen.aliases | selectattr("is_public") | list -%}
+ {% if public_aliases and public_aliases[1:] %}
+
+ For backwards compatibility
+ {% set fullpath = public_aliases[0].target_module + "." + public_aliases[0].alias_name %}
+ {{fullpath}}
+ is still available as
+ {% for alias in public_aliases[1:] -%}
+ {%- set fullpath = alias.target_module + "." + alias.alias_name -%}
+ {{fullpath}}
+ {%- if not loop.last %}, {% endif %}
+ {%- endfor %}.
+ {% if public_aliases[2:] %}
+ The use of these aliases is discouraged as they are to be deprecated.
+ {% else %}
+ The use of this alias is discouraged as it is to be deprecated.
+ {% endif %}
+
+ {% endif %}
+ {% endif %}
+ {% endblock aliases %}
+
+ {% block docstring scoped %}
+ {#- Docstring block.
+
+ This block renders the docstring for the function.
+ -#}
+ {% with docstring_sections = function.docstring.parsed %}
+ {% include "docstring.html.jinja" with context %}
+ {% endwith %}
+ {% endblock docstring %}
+
+ {% block source scoped %}
+ {#- Source block.
+
+ This block renders the source code for the function.
+ -#}
+ {% if config.show_source and function.source %}
+
+ {{ lang.t("Source code in") }}
+ {%- if function.relative_filepath.is_absolute() -%}
+ {{ function.relative_package_filepath }}
+ {%- else -%}
+ {{ function.relative_filepath }}
+ {%- endif -%}
+
+ {{ function.source|highlight(language="python", linestart=function.lineno or 0, linenums=True) }}
+
+ {% endif %}
+ {% endblock source %}
+ {% endblock contents %}
+
+
+ {% endwith %}
+
diff --git a/docs/templates/python/material/module.html.jinja b/docs/templates/python/material/module.html.jinja
index a61acfda9b..1fb1cb6950 100644
--- a/docs/templates/python/material/module.html.jinja
+++ b/docs/templates/python/material/module.html.jinja
@@ -1,12 +1,131 @@
-{% extends "_base/module.html.jinja" %}
+{#- Template for Python modules.
-{% block heading scoped %}
- {% block source_link scoped %}
- {% if config.extra.link_source and module.source_link %}
- [source]
+This template renders a Python module.
+
+Context:
+ module (griffe.Module): The module to render.
+ root (bool): Whether this is the root object, injected with `:::` in a Markdown page.
+ heading_level (int): The HTML heading level to use.
+ config (dict): The configuration options.
+-#}
+
+{% block logs scoped %}
+ {#- Logging block.
+
+ This block can be used to log debug messages, deprecation messages, warnings, etc.
+ -#}
+ {{ log.debug("Rendering " + module.path) }}
+{% endblock logs %}
+
+
+ {% with obj = module, html_id = module.path %}
+
+ {% if root %}
+ {% set show_full_path = config.show_root_full_path %}
+ {% set root_members = True %}
+ {% elif root_members %}
+ {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %}
+ {% set root_members = False %}
+ {% else %}
+ {% set show_full_path = config.show_object_full_path %}
{% endif %}
- {% endblock source_link %}
- {{ super() }}
+ {% set module_name = module.path if show_full_path else module.name %}
+
+ {% if not root or config.show_root_heading %}
+ {% filter heading(
+ heading_level,
+ role="module",
+ id=html_id,
+ class="doc doc-heading",
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else module.name),
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+
+ {% block heading scoped %}
+ {#- Heading block.
+
+ This block renders the heading for the module.
+ -#}
+ {% block source_link scoped %}
+ {% if config.extra.link_source and module.source_link %}
+
[source]
+ {% endif %}
+ {% endblock source_link %}
+
+ {% if config.show_symbol_type_heading %}
{% endif %}
+ {% if config.heading and root %}
+ {{ config.heading }}
+ {% elif config.separate_signature %}
+
{{ module_name }}
+ {% else %}
+
{{ module_name }}
+ {% endif %}
+ {% endblock heading %}
+
+ {% block labels scoped %}
+ {#- Labels block.
+
+ This block renders the labels for the module.
+ -#}
+ {% with labels = module.labels %}
+ {% include "labels.html.jinja" with context %}
+ {% endwith %}
+ {% endblock labels %}
+
+ {% endfilter %}
+
+ {% else %}
+ {% if config.show_root_toc_entry %}
+ {% filter heading(heading_level,
+ role="module",
+ id=html_id,
+ toc_label=('
'|safe if config.show_symbol_type_toc else '') + (config.toc_label if config.toc_label and root else module.name),
+ hidden=True,
+ skip_inventory=config.skip_local_inventory,
+ ) %}
+ {% endfilter %}
+ {% endif %}
+ {% set heading_level = heading_level - 1 %}
+ {% endif %}
+
+
+ {% block contents scoped %}
+ {#- Contents block.
+
+ This block renders the contents of the module.
+ It contains other blocks that users can override.
+ Overriding the contents block allows to rearrange the order of the blocks.
+ -#}
+ {% block docstring scoped %}
+ {#- Docstring block.
+
+ This block renders the docstring for the module.
+ -#}
+ {% with docstring_sections = module.docstring.parsed %}
+ {% include "docstring.html.jinja" with context %}
+ {% endwith %}
+ {% endblock docstring %}
+
+ {% block summary scoped %}
+ {#- Summary block.
+
+ This block renders auto-summaries for classes, methods, and attributes.
+ -#}
+ {% include "summary.html.jinja" with context %}
+ {% endblock summary %}
+
+ {% block children scoped %}
+ {#- Children block.
+
+ This block renders the children (members) of the module.
+ -#}
+ {% set root = False %}
+ {% set heading_level = heading_level + 1 %}
+ {% include "children.html.jinja" with context %}
+ {% endblock children %}
+ {% endblock contents %}
+
-{% endblock heading %}
+ {% endwith %}
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 0758f36b67..ac8f38eec9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -327,6 +327,7 @@ extra_javascript:
plugins:
- search
+ - autorefs
- minify:
minify_html: true
minify_css: true
@@ -358,6 +359,7 @@ plugins:
docstring_section_style: spacy
annotations_path: source
filters: public
+ backlinks: flat
extra:
link_source: true
extensions:
diff --git a/requirements-docs.txt b/requirements-docs.txt
index ad8d20e1e0..40215a7826 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -4,8 +4,9 @@ mike==2.1.3
markdown==3.9
pymdown-extensions==10.17.2
mkdocs-minify-plugin>=0.2.0
-hopsworks-apigen@git+https://github.com/aversey/hopsworks-aliases.git
-mkdocstrings[python]==1.0.0
-mkdocstrings-python==2.0.1
+hopsworks-apigen@git+https://github.com/logicalclocks/hopsworks-apigen.git
+mkdocstrings[python]==1.0.3
+mkdocstrings-python==2.0.2
+mkdocs-autorefs==1.4.4
ruff==0.15.0
linkchecker
From e8814d7d3d4df3aef27a9595a821c0acecbd4a63 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Tue, 17 Feb 2026 16:28:12 +0100
Subject: [PATCH 10/15] Use PyPI version of hopsworks-apigen
---
requirements-docs.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-docs.txt b/requirements-docs.txt
index 40215a7826..3e50579236 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -4,7 +4,7 @@ mike==2.1.3
markdown==3.9
pymdown-extensions==10.17.2
mkdocs-minify-plugin>=0.2.0
-hopsworks-apigen@git+https://github.com/logicalclocks/hopsworks-apigen.git
+hopsworks-apigen==1.0.0
mkdocstrings[python]==1.0.3
mkdocstrings-python==2.0.2
mkdocs-autorefs==1.4.4
From 4ebf37603f869aae4d5558880f9ae3a83858857b Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 18 Feb 2026 17:43:25 +0100
Subject: [PATCH 11/15] Change back to the official API repo
---
.github/workflows/mkdocs-test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/mkdocs-test.yml b/.github/workflows/mkdocs-test.yml
index 93a8527f61..fa2c942f10 100644
--- a/.github/workflows/mkdocs-test.yml
+++ b/.github/workflows/mkdocs-test.yml
@@ -15,7 +15,7 @@ jobs:
- name: Checkout the API repo
uses: actions/checkout@v4
with:
- repository: aversey/hopsworks-api
+ repository: logicalclocks/hopsworks-api
ref: ${{ github.base_ref }}
path: hopsworks-api
From d6c999ff9d12ed68e79503600940ff92891cf574 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Wed, 18 Feb 2026 17:47:15 +0100
Subject: [PATCH 12/15] In release workflow too
---
.github/workflows/mkdocs-release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/mkdocs-release.yml b/.github/workflows/mkdocs-release.yml
index 4a751fc7d7..63ffc0f23f 100644
--- a/.github/workflows/mkdocs-release.yml
+++ b/.github/workflows/mkdocs-release.yml
@@ -59,7 +59,7 @@ jobs:
- name: Checkout the API repo
uses: actions/checkout@v4
with:
- repository: aversey/hopsworks-api
+ repository: logicalclocks/hopsworks-api
ref: ${{ env.BRANCH }}
path: hopsworks-api
From dbd6dcfbefc998b03916036532d42a8af9a8e9a2 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Fri, 20 Feb 2026 15:21:30 +0100
Subject: [PATCH 13/15] Unhide logs of mkdocs
---
.github/workflows/mkdocs-test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/mkdocs-test.yml b/.github/workflows/mkdocs-test.yml
index fa2c942f10..34f51f1ad4 100644
--- a/.github/workflows/mkdocs-test.yml
+++ b/.github/workflows/mkdocs-test.yml
@@ -64,7 +64,7 @@ jobs:
- name: Check for broken links
run: |
# run the server
- mkdocs serve > /dev/null 2>&1 &
+ mkdocs serve 2>&1 &
SERVER_PID=$!
echo "mk server in PID $SERVER_PID"
# Give enough time for deployment (2min max)
From c4695897728f3cbc93595e2ba966301817002eaf Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Fri, 20 Feb 2026 15:25:13 +0100
Subject: [PATCH 14/15] Quiet mkdocs
---
.github/workflows/mkdocs-test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/mkdocs-test.yml b/.github/workflows/mkdocs-test.yml
index 34f51f1ad4..4ad6b53a79 100644
--- a/.github/workflows/mkdocs-test.yml
+++ b/.github/workflows/mkdocs-test.yml
@@ -64,7 +64,7 @@ jobs:
- name: Check for broken links
run: |
# run the server
- mkdocs serve 2>&1 &
+ mkdocs serve -q &
SERVER_PID=$!
echo "mk server in PID $SERVER_PID"
# Give enough time for deployment (2min max)
From 1c333342715010e2314f300c91c35efbd54579fe Mon Sep 17 00:00:00 2001
From: Aleksey Veresov
Date: Fri, 20 Feb 2026 15:51:03 +0100
Subject: [PATCH 15/15] Remove docs group
---
.github/workflows/mkdocs-release.yml | 2 +-
.github/workflows/mkdocs-test.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/mkdocs-release.yml b/.github/workflows/mkdocs-release.yml
index 63ffc0f23f..593420a801 100644
--- a/.github/workflows/mkdocs-release.yml
+++ b/.github/workflows/mkdocs-release.yml
@@ -92,7 +92,7 @@ jobs:
working-directory: hopsworks-api/python
- name: Install Python API dependencies
- run: uv sync --extra dev --group docs --project hopsworks-api/python
+ run: uv sync --extra dev --project hopsworks-api/python
- name: Install Python dependencies
run: uv pip install -r requirements-docs.txt
diff --git a/.github/workflows/mkdocs-test.yml b/.github/workflows/mkdocs-test.yml
index 4ad6b53a79..c86687a2d3 100644
--- a/.github/workflows/mkdocs-test.yml
+++ b/.github/workflows/mkdocs-test.yml
@@ -53,7 +53,7 @@ jobs:
working-directory: hopsworks-api/python
- name: Install Python API dependencies
- run: uv sync --extra dev --group docs --project hopsworks-api/python
+ run: uv sync --extra dev --project hopsworks-api/python
- name: Install Python dependencies
run: uv pip install -r requirements-docs.txt