Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ instance/

# pdoc documentation
/doc
/docusaurus

# PyBuilder
.pybuilder/
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-test.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
fishjam:
image: "ghcr.io/fishjam-cloud/fishjam:${TAG:-edge}"
image: "ghcr.io/fishjam-cloud/fishjam:${TAG:-edge-88258de-1764324482}"
container_name: fishjam
restart: on-failure
healthcheck:
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ format_check = "scripts:run_format_check"
lint = "scripts:run_linter"
fix_lint = "scripts:run_linter_fix"
generate_docs = "scripts:generate_docs"
generate_docusaurus = "scripts:generate_docusaurus"
update_client = "scripts:update_client"
room_manager = "scripts:start_room_manager"

Expand Down
75 changes: 73 additions & 2 deletions scripts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import shutil
import subprocess
import sys
Expand Down Expand Up @@ -55,6 +56,12 @@ def run_linter_fix():


def generate_docs():
here = Path(__file__).parent
input = here / "doc"

if input.exists():
shutil.rmtree(input)

check_exit_code(
"pdoc \
--include-undocumented \
Expand All @@ -64,8 +71,6 @@ def generate_docs():
-o doc \
fishjam"
)
here = Path(__file__).parent
input = here / "doc"
input_images = here / "images"
out = here / "docs" / "api"
out_images = here / "docs" / "api" / "images"
Expand All @@ -81,6 +86,72 @@ def generate_docs():
f.rename(f.with_suffix(".md"))


def clean_mdx_content(content: str) -> str:
parts = re.split(r"((?:```[\s\S]*?```|`[^`\n]+`))", content)

# example: convert `fishjam._openapi_client.models.peer.Peer` into `Peer`
internal_path_pattern = r"fishjam\.(?:[\w.]+\.)?_[\w.]+\."

cleaned_parts = []
for part in parts:
if part.startswith("`"):
text = (
part.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&#39;", "'")
.replace("builtins.", "")
)

text = re.sub(internal_path_pattern, "", text)

cleaned_parts.append(text)
else:
text = part.replace("{", "\\{").replace("}", "\\}").replace("<", "&lt;")

cleaned_parts.append(text)

return "".join(cleaned_parts)


def generate_docusaurus():
here = Path(__file__).parent
input = here / "doc"
out = here / "docusaurus"

if input.exists():
shutil.rmtree(input)
if out.exists():
shutil.rmtree(out)

check_exit_code(
"pdoc \
--include-undocumented \
-t templates/docusaurus \
-o doc \
fishjam"
)
try:
os.remove(input / "index.html")
except FileNotFoundError:
pass

out.mkdir(parents=True, exist_ok=True)

for f in input.glob("**/*.html"):
content = f.read_text(encoding="utf-8")
safe_content = clean_mdx_content(content)
rel_path = f.relative_to(input)
dest_path = out / rel_path.with_suffix(".md")
dest_path.parent.mkdir(parents=True, exist_ok=True)

dest_path.write_text(safe_content, encoding="utf-8")

fishjam_dir = out / "fishjam"
submodules_dir = out / "submodules"

fishjam_dir.rename(submodules_dir)


def update_client():
if len(sys.argv) < 2:
raise RuntimeError("Missing fishjam openapi.yaml raw url positional argument")
Expand Down
7 changes: 7 additions & 0 deletions templates/docusaurus/frame.html.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: {{ module.name }}
sidebar_label: {{ module.name.split(".")[-1] }}
custom_edit_url: null
---

{% block content %}{% endblock %}
144 changes: 144 additions & 0 deletions templates/docusaurus/module.html.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{% extends "frame.html.jinja2" %}

{% block content %}
# {{ module.name }}

{% block module_info %}
{% if module.docstring and ".. include::" not in module.docstring %}
{{ module.docstring | safe }}
{% endif %}
{% endblock %}

{% block nav_submodules %}
{% if module.submodules %}
## Submodules
{% for submodule in module.submodules if is_public(submodule) | trim %}
- [{{ submodule.name }}](submodules/{{ submodule.name }})
{% endfor %}
{% endif %}
{% endblock %}

{% block module_contents %}
{% for m in module.flattened_own_members if is_public(m) | trim %}
## {{ m.name }}
{{ member(m) }}
{% if m.kind == "class" %}
{% for cm in m.own_members if cm.kind != "class" and is_public(cm) | trim %}
### {{ cm.name }}
{{ member(cm) }}
{% endfor %}
{% set inherited_members = inherited(m) | trim %}
{% if inherited_members %}
#### Inherited Members
{{ inherited_members }}
{% endif %}
{% endif %}
---
{% endfor %}
{% endblock %}
{% endblock content %}

{#
=========================================================================
HELPER MACROS (Markdown Version)
=========================================================================
#}

{% defaultmacro default_value(var) -%}
{% if var.default_value_str and "object object" not in var.default_value_str %}
= {{ var.default_value_str | safe }}
{% endif %}
{% enddefaultmacro %}

{% defaultmacro annotation(var) %}
{% if var.annotation_str %}{{ var.annotation_str }}{% endif %}
{% enddefaultmacro %}

{% defaultmacro function(fn) -%}
{% if fn.name == "__init__" %}
def {{ fn.qualname.split(".")[-1] }}{{ fn.signature_without_self | string | safe }}
{% else %}
def {{ fn.name }}{{ fn.signature | string | safe }}
{% endif %}
{% enddefaultmacro %}

{% defaultmacro variable(var) -%}
{{ var.name }}{{ annotation(var) }}{{ default_value(var) }}
{% enddefaultmacro %}

{% defaultmacro class_bases(cls) %}
{%- if cls.bases -%}
(
{%- for base in cls.bases -%}
{%- if base is mapping or base is iterable and base is not string -%}
{{ base[-1] }}
{%- else -%}
{{ base }}
{%- endif -%}
{%- if loop.nextitem %}, {% endif %}
{%- endfor -%}
)
{%- endif -%}
{% enddefaultmacro %}

{% defaultmacro class(cls) -%}
class {{ cls.qualname }}{{ class_bases(cls) }}:
{% enddefaultmacro %}

{% defaultmacro member(doc) %}
```python
{% if doc.kind == "class" %}{{ class(doc) }}{% elif doc.kind == "function" %}{{ function(doc) }}{% else %}{{ variable(doc) }}{% endif %}
```
{{ doc.docstring | safe }}
{% enddefaultmacro %}

{% defaultmacro is_public(doc) %}
{% if not include_undocumented and not doc.docstring %}
{% elif doc.docstring and "@private" in doc.docstring %}
{% elif doc.name == "__init__" and (doc.docstring or (doc.kind == "function" and doc.signature_without_self.parameters)) %}
true
{% elif doc.name == "__doc__" %}
{% elif doc.kind == "variable" and doc.is_typevar and not doc.docstring %}
{% elif doc.kind == "module" and doc.fullname not in all_modules %}
{% elif (doc.qualname or doc.name) is in(module.obj.__all__ or []) %}
true
{% elif not doc.name.startswith("_") %}
true
{% endif %}
{% enddefaultmacro %}

{% defaultmacro inherited(cls) %}
{% set ignored_bases = ["str", "object", "int", "float", "bool", "list", "dict", "tuple", "set", "exception", "baseexception"] %}
{% for base, members in cls.inherited_members.items() %}
{% set base_name = base.name if base.name is defined else base %}
{% if base_name is mapping or base_name is iterable and base_name is not string %}
{% set base_name = base_name[-1] %}
{% endif %}

{% if base_name | lower not in ignored_bases %}

{% set member_list %}
{% for m in members if is_public(m) | trim %}
* `{{ m.name }}`
{% endfor %}
{% endset %}

{% if member_list | trim %}
* **{{ base_name }}**:
{{ member_list }}
{% endif %}

{% endif %}
{% endfor %}
{% enddefaultmacro %}

{# Empty macros to prevent errors from unused calls in default logic #}
{% defaultmacro bases(cls) %}{% enddefaultmacro %}
{% defaultmacro decorators(doc) %}{% enddefaultmacro %}
{% defaultmacro submodule(mod) %}{% enddefaultmacro %}
{% defaultmacro docstring(var) %}{% enddefaultmacro %}
{% defaultmacro nav_members(members) %}{% enddefaultmacro %}
{% defaultmacro view_source_state(doc) %}{% enddefaultmacro %}
{% defaultmacro view_source_button(doc) %}{% enddefaultmacro %}
{% defaultmacro view_source_code(doc) %}{% enddefaultmacro %}
{% defaultmacro module_name() %}{% enddefaultmacro %}
2 changes: 2 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.