Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jobs:
run: |
pip install python-dotenv
python scripts/check_snippet_coverage.py

- name: Generate Documentation
run: |
make docs

- name: Lint and format check with Ruff
run: |
Expand Down
82 changes: 82 additions & 0 deletions .github/workflows/documentation-upload.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Generate and upload documentation

on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version to use for the documentation package in semver format (e.g. 1.2.3)'
required: true

jobs:
upload-documentation:
runs-on: ubuntu-latest
env:
SDK_NAME: sinch-sdk-python

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Resolve Version
id: version
run: |
if [ "${{ github.event_name }}" = "release" ]; then
VERSION="${{ github.event.release.tag_name }}"
else
VERSION="${{ inputs.version }}"
fi
# Strip leading 'v' if present (e.g. v1.2.3 → 1.2.3)
VERSION="${VERSION#v}"
echo "value=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Validate Version Format
run: |
VERSION="${{ steps.version.outputs.value }}"
SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((alpha|beta|preview)(\.[0-9]+)?))?$'
if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then
echo "::error::Invalid version format: '$VERSION'. Expected semver (e.g. 1.2.3, 1.2.3-alpha, 1.2.3-beta.1, 1.2.3-preview)"
exit 1
fi
echo "Version '$VERSION' is valid"

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Generate Documentation
run: |
make docs
Comment thread
JPPortier marked this conversation as resolved.

- name: Package Documentation
run: |
cd docs/build/html
zip -r "../../../${{ env.SDK_NAME }}-${{ steps.version.outputs.value }}.zip" .

- name: Upload to GitLab Registry
run: |
echo "Uploading documentation package to GitLab Registry..."
VERSION="${{ steps.version.outputs.value }}"
curl --fail --show-error --location --header "PRIVATE-TOKEN: ${{ secrets.GITLAB_REGISTRY_UPLOAD_DOC_TOKEN }}" \
--upload-file "./${{ env.SDK_NAME }}-${VERSION}.zip" \
"https://gitlab.com/api/v4/projects/63164411/packages/generic/${{ env.SDK_NAME }}/${VERSION}/${{ env.SDK_NAME }}-${VERSION}.zip"
echo "Documentation package for version ${VERSION} uploaded to GitLab Registry"

- name: Trigger Downstream GitLab Pipeline
run: |
echo "Triggering downstream GitLab pipeline to notify about new documentation package version..."
VERSION="${{ steps.version.outputs.value }}"
curl --fail --show-error --location --request POST \
--form "token=${{ secrets.GITLAB_NOTIFY_REGISTRY_UPLOADED_DOC_TOKEN }}" \
--form "ref=main" \
--form "variables[UPSTREAM_PACKAGE_NAME]=${{ env.SDK_NAME }}" \
--form "variables[UPSTREAM_PACKAGE_VERSION]=${VERSION}" \
"https://gitlab.com/api/v4/projects/63164411/trigger/pipeline"
echo "Documentation repo notified about new package version ${VERSION}"
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ instance/
.scrapy

# Sphinx documentation
docs/_build/
docs/build/
docs/api/


# PyBuilder
.pybuilder/
Expand Down
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.PHONY: docs

docs:
rm -rf docs/build
rm -rf docs/api
sphinx-apidoc --force --separate --no-toc --maxdepth 2 \
--templatedir docs/_templates/apidoc -o docs/api sinch \
"sinch/core/models/base_model.py" \
"sinch/core/models/utils.py" \
"sinch/core/deserializers.py" \
"sinch/core/endpoint.py" \
"sinch/core/enums.py" \
"sinch/core/types.py" \
"sinch/domains/sms/enums.py" \
"sinch/*/internal" "sinch/*/internal/*" \
"sinch/*/api/v1/base" "sinch/*/api/v1/base/*" \
"sinch/*/api/v1/utils" "sinch/*/api/v1/utils/*" \
"sinch/domains/authentication/endpoints" "sinch/domains/authentication/endpoints/*" \
"sinch/domains/authentication/sinch_events" "sinch/domains/authentication/sinch_events/*" \
"sinch/domains/numbers/models/v1/utils" "sinch/domains/numbers/models/v1/utils/*"
sphinx-build -b html docs docs/build/html
21 changes: 21 additions & 0 deletions docs/_static/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* ---------------------------------------------------------------------------
Multi-line signatures (one parameter per line).

When a signature is wrapped, Sphinx renders each parameter as a <dd> inside
a nested <dl>. The Read the Docs theme styles every <dl>/<dd> with borders,
background tint and vertical margins, which leak into the signature and draw
ugly "lines" between parameters. Flatten that nested list so the parameters
read as a clean indented column.
--------------------------------------------------------------------------- */
.rst-content .sig dl,
.rst-content .sig dd {
margin: 0;
padding: 0;
border: none;
background: none;
}

/* Keep each parameter indented under the opening parenthesis. */
.rst-content .sig dd {
margin-left: 2em;
}
8 changes: 8 additions & 0 deletions docs/_templates/apidoc/module.rst.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{%- if show_headings %}
{{- [basename, "module"] | join(" ") | e | heading }}

{% endif -%}
.. automodule:: {{ qualname }}
{%- for option in automodule_options %}
:{{ option }}:
{%- endfor %}
39 changes: 39 additions & 0 deletions docs/_templates/apidoc/package.rst.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{%- macro automodule(modname, options) -%}
.. automodule:: {{ modname }}
{%- for option in options %}
:{{ option }}:
{%- endfor %}
{%- endmacro %}

{%- macro toctree(docnames) -%}
.. toctree::
:maxdepth: {{ maxdepth }}
{% for docname in docnames %}
{{ docname }}
{%- endfor %}
{%- endmacro %}

{{- [pkgname, "package"] | join(" ") | e | heading }}

{%- if is_namespace %}
.. py:module:: {{ pkgname }}
{% endif %}

{%- if subpackages %}
{{ toctree(subpackages) }}
{% endif %}

{%- if submodules %}
{% if separatemodules %}
{{ toctree(submodules) }}
{% else %}
{% for submodule in submodules %}
{{ [submodule.split(".")[-1], "module"] | join(" ") | e | heading(2) }}
{{ automodule(submodule, automodule_options) }}
{% endfor %}
{%- endif %}
{%- endif %}

{%- if not is_namespace %}
{{ automodule(pkgname, automodule_options) }}
{% endif %}
62 changes: 62 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import sys

# Allow autodoc to import the sinch package from the project root
sys.path.insert(0, os.path.abspath(".."))
from sinch import __version__

# -- Project information -------------------------------------------------------

project = "Sinch Python SDK"
copyright = "2026, Sinch Developer Experience Team"
author = "Sinch Developer Experience Team"
release = __version__

# -- General configuration -----------------------------------------------------

extensions = [
# Pulls docstrings from Python source into the generated .rst files
"sphinx.ext.autodoc",
# Adds [source] links that open the highlighted source file
"sphinx.ext.viewcode",
]

# The .rst files under api/ are generated by the `sphinx-apidoc` CLI invoked
# from the Makefile (`make docs`), using the custom templates in
# _templates/apidoc/ to strip the "package" suffix and the
# "Subpackages"/"Submodules"/"Module contents" headings. The
# sphinx.ext.apidoc extension is intentionally NOT used because it ignores
# the template directory.

# -- sphinx.ext.autodoc --------------------------------------------------------

autodoc_default_options = {
# Document all public members (methods, attributes, nested classes)
"members": True,
'undoc-members': True,
# Show the class inheritance chain
"show-inheritance": True,
# Preserve the order in which members appear in the source file
"member-order": "bysource",
}

# Render type hints as part of the parameter/return descriptions, not the signature
autodoc_typehints = "both"
python_maximum_signature_line_length = 88

# -- HTML output ---------------------------------------------------------------

html_theme = "sphinx_rtd_theme"

html_theme_options = {
# Maximum depth of the navigation sidebar (-1 = unlimited)
"navigation_depth": -1,
# Keep all navigation entries expanded by default
"collapse_navigation": False,
}

# Directory with extra static files (custom CSS, etc.), relative to this conf.py
html_static_path = ["_static"]

# Extra stylesheets loaded after the theme's own CSS
html_css_files = ["custom.css"]
8 changes: 8 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Sinch Python SDK
================

.. toctree::
:maxdepth: 3
:caption: API Reference

api/sinch
7 changes: 6 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ ruff
requests

# Data Validation
pydantic >= 2.0.0
pydantic >= 2.0.0

# Documentation
# Sphinx 7.1 introduced python_maximum_signature_line_length and 7.x is also the last series supporting Python 3.9.
sphinx >= 7.1
sphinx-rtd-theme >= 2.0
Empty file.
Empty file.
Empty file.
Empty file.
Loading