Skip to content

Commit d15cc0b

Browse files
Sphinx docs using AutoAPI (#19)
* Sphinx docs using AutoAPI * Add create documentation workflow --------- Co-authored-by: MasloMaslane <[email protected]>
1 parent 0e81f22 commit d15cc0b

File tree

21 files changed

+392
-67
lines changed

21 files changed

+392
-67
lines changed

.github/workflows/docs.yml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Create documentation
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- name: Set up Python 3.13
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: '3.13'
18+
- name: Install pip dependencies
19+
run: |
20+
pip install -e .[docs]
21+
- name: Run Sphinx
22+
run: |
23+
cd docs
24+
make html
25+
- name: Upload HTML artifact
26+
uses: actions/upload-pages-artifact@v3
27+
with:
28+
path: docs/_build/html
29+
30+
deploy:
31+
needs: build
32+
runs-on: ubuntu-latest
33+
permissions:
34+
pages: write
35+
id-token: write
36+
environment:
37+
name: github-pages
38+
url: ${{ steps.deployment.outputs.page_url }}
39+
steps:
40+
- name: Deploy to GitHub Pages
41+
id: deployment
42+
uses: actions/deploy-pages@v4

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ coverage.xml
1616
.DS_Store
1717

1818
# Files from Django db
19-
/sio3pack
19+
/sio3pack
20+
21+
# Sphinx documentation
22+
docs/_build/

docs/Makefile

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line, and also
5+
# from the environment for the first two.
6+
SPHINXOPTS ?=
7+
SPHINXBUILD ?= sphinx-build
8+
SOURCEDIR = .
9+
BUILDDIR = _build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
.PHONY: help Makefile
16+
17+
# Catch-all target: route all unknown targets to Sphinx using the new
18+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+
%: Makefile
20+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

docs/conf.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Configuration file for the Sphinx documentation builder.
2+
#
3+
# For the full list of built-in configuration values, see the documentation:
4+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
5+
6+
# -- Project information -----------------------------------------------------
7+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8+
9+
import sio3pack
10+
11+
project = 'SIO3Pack'
12+
copyright = '2025, Tomasz Kwiatkowski, Mateusz Masiarz, Jakub Rożek, Stanisław Struzik'
13+
author = 'Tomasz Kwiatkowski, Mateusz Masiarz, Jakub Rożek, Stanisław Struzik'
14+
release = sio3pack.__version__
15+
16+
# -- General configuration ---------------------------------------------------
17+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
18+
19+
extensions = [
20+
'autoapi.extension',
21+
'sphinx.ext.autodoc', # Also required by AutoAPI.
22+
'sphinx.ext.viewcode',
23+
]
24+
25+
templates_path = ['_templates']
26+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
27+
28+
autoapi_options = [
29+
"members",
30+
"undoc-members",
31+
"show-inheritance",
32+
"special-members",
33+
"imported-members",
34+
]
35+
autoapi_dirs = ['../src/sio3pack/']
36+
# Additional objects to include.
37+
autoapi_include = [
38+
"sio3pack.django.common.handler.DjangoHandler",
39+
"sio3pack.django.sinolpack.handler.SinolpackDjangoHandler",
40+
]
41+
autodoc_typehints = 'description'
42+
43+
def should_skip_submodule(app, what, name, obj, skip, options):
44+
if what == "module":
45+
skip = True
46+
if what == "attribute":
47+
skip = True
48+
49+
for object in autoapi_include:
50+
# Include everything from object.
51+
if name.startswith(object):
52+
skip = False
53+
# Include every parent modules.
54+
if object.startswith(name):
55+
skip = False
56+
57+
submodule = name.split(".")[-1]
58+
# Don't show private objects.
59+
if submodule.startswith("_"):
60+
skip = True
61+
# Skip django migrations.
62+
if submodule in ["migrations"]:
63+
skip = True
64+
return skip
65+
66+
def setup(sphinx):
67+
sphinx.connect("autoapi-skip-member", should_skip_submodule)
68+
69+
# -- Options for HTML output -------------------------------------------------
70+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
71+
72+
html_theme = 'furo'
73+
html_static_path = ['_static']

docs/index.rst

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. SIO3Pack documentation master file.
2+
3+
SIO3Pack documentation
4+
======================
5+
6+
7+
.. toctree::
8+
:maxdepth: 2
9+
:caption: Contents:
10+

setup.cfg

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ django =
4040
vis =
4141
dash
4242
dash-cytoscape
43+
docs =
44+
sphinx
45+
sphinx-autoapi
46+
furo
4347

4448
[options.entry_points]
4549
console_scripts =

src/sio3pack/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
__version__ = "1.0.0.dev1"
22

33
from sio3pack.files import LocalFile
4-
from sio3pack.packages.exceptions import ImproperlyConfigured, PackageAlreadyExists
4+
from sio3pack.packages.exceptions import *
55
from sio3pack.packages.package import Package
66

7+
__all__ = ["from_file", "from_db"]
8+
79

810
def from_file(file: str | LocalFile, django_settings=None) -> Package:
911
"""
1012
Initialize a package object from a file (archive or directory).
13+
1114
:param file: The file path or File object.
1215
:param django_settings: Django settings object.
1316
:return: The package object.
@@ -22,6 +25,7 @@ def from_db(problem_id: int) -> Package:
2225
Initialize a package object from the database.
2326
If sio3pack isn't installed with Django support, it should raise an ImproperlyConfigured exception.
2427
If there is no package with the given problem_id, it should raise an UnknownPackageType exception.
28+
2529
:param problem_id: The problem id.
2630
:return: The package object.
2731
"""

src/sio3pack/django/common/handler.py

+33-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,27 @@
33
from django.core.files import File
44
from django.db import transaction
55

6+
import sio3pack
67
from sio3pack.django.common.models import SIO3Package, SIO3PackModelSolution, SIO3PackNameTranslation, SIO3PackStatement
7-
from sio3pack.files.local_file import LocalFile
8-
from sio3pack.files.remote_file import RemoteFile
8+
from sio3pack.files import LocalFile, RemoteFile
99
from sio3pack.packages.exceptions import ImproperlyConfigured, PackageAlreadyExists
1010

1111

1212
class DjangoHandler:
13-
def __init__(self, package: Type["Package"], problem_id: int):
13+
"""
14+
Base class for handling Django models.
15+
Allows to save the package to the database and retrieve its data.
16+
17+
:param sio3pack.Package package: The package to handle.
18+
:param int problem_id: The problem ID.
19+
"""
20+
21+
def __init__(self, package: "sio3pack.Package", problem_id: int):
22+
"""
23+
Initialize the handler with the package and problem ID.
24+
:param sio3pack.Package package: The package to handle.
25+
:param int problem_id: The problem ID.
26+
"""
1427
self.package = package
1528
self.problem_id = problem_id
1629
try:
@@ -71,20 +84,37 @@ def _add_statement(language: str, statement: LocalFile):
7184

7285
@property
7386
def short_name(self) -> str:
87+
"""
88+
Short name of the problem.
89+
"""
7490
return self.db_package.short_name
7591

7692
@property
7793
def full_name(self) -> str:
94+
"""
95+
Full name of the problem.
96+
"""
7897
return self.db_package.full_name
7998

8099
@property
81100
def lang_titles(self) -> dict[str, str]:
101+
"""
102+
A dictionary of problem titles,
103+
where keys are language codes and values are titles.
104+
"""
82105
return {t.language: t.name for t in self.db_package.name_translations.all()}
83106

84107
@property
85108
def model_solutions(self) -> list[dict[str, Any]]:
109+
"""
110+
A list of model solutions, where each element is a dictionary containing
111+
a :class:`sio3pack.RemoteFile` object.
112+
"""
86113
return [{"file": RemoteFile(s.source_file.path)} for s in self.db_package.model_solutions.all()]
87114

88115
@property
89116
def lang_statements(self) -> dict[str, RemoteFile]:
117+
"""
118+
A dictionary of problem statements, where keys are language codes and values are files.
119+
"""
90120
return {s.language: RemoteFile(s.content.path) for s in self.db_package.statements.all()}

src/sio3pack/django/sinolpack/handler.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@
1111
SinolpackConfig,
1212
SinolpackModelSolution,
1313
)
14-
from sio3pack.files.remote_file import RemoteFile
14+
from sio3pack.files import RemoteFile
1515
from sio3pack.packages.sinolpack.enums import ModelSolutionKind
1616

1717

1818
class SinolpackDjangoHandler(DjangoHandler):
19+
"""
20+
Handler for Sinolpack packages in Django.
21+
Has additional properties like config, model_solutions, additional_files and attachments.
22+
"""
1923

20-
def __init__(self, package: Type["Package"], problem_id: int):
24+
def __init__(self, package: "sio3pack.Sinolpack", problem_id: int):
2125
super().__init__(package, problem_id)
2226

2327
@transaction.atomic
@@ -69,17 +73,30 @@ def _save_attachments(self):
6973

7074
@property
7175
def config(self) -> dict[str, Any]:
76+
"""
77+
Config file of the package.
78+
"""
7279
return self.db_package.config.parsed_config
7380

7481
@property
7582
def model_solutions(self) -> list[dict[str, Any]]:
83+
"""
84+
A list of model solutions, where each element is a dictionary containing a :class:`sio3pack.RemoteFile` object
85+
and the :class:`sio3pack.packages.sinolpack.enums.ModelSolutionKind` kind.
86+
"""
7687
solutions = SinolpackModelSolution.objects.filter(package=self.db_package)
7788
return [{"file": RemoteFile(s.source_file.path), "kind": s.kind} for s in solutions]
7889

7990
@property
8091
def additional_files(self) -> list[RemoteFile]:
92+
"""
93+
A list of additional files (as :class:`sio3pack.RemoteFile`) for the problem.
94+
"""
8195
return [RemoteFile(f.file.path) for f in self.db_package.additional_files.all()]
8296

8397
@property
8498
def attachments(self) -> list[RemoteFile]:
99+
"""
100+
A list of attachments (as :class:`sio3pack.RemoteFile`) related to the problem.
101+
"""
85102
return [RemoteFile(f.content.path) for f in self.db_package.attachments.all()]

src/sio3pack/files/file.py

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
class File:
22
"""
33
Base class for all files in a package.
4+
5+
:param str path: The path to the file.
46
"""
57

68
def __init__(self, path: str):
@@ -10,7 +12,17 @@ def __str__(self):
1012
return f"<{self.__class__.__name__} {self.path}>"
1113

1214
def read(self) -> str:
15+
"""
16+
Read the file content.
17+
18+
:return: The content of the file.
19+
"""
1320
raise NotImplementedError()
1421

1522
def write(self, text: str):
23+
"""
24+
Write to the file.
25+
26+
:param str text: The text to write.
27+
"""
1628
raise NotImplementedError()

src/sio3pack/files/local_file.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55

66
class LocalFile(File):
77
"""
8-
Base class for all files in a package that are stored locally.
8+
Base class for a file in a package that is stored locally.
99
"""
1010

1111
@classmethod
1212
def get_file_matching_extension(cls, dir: str, filename: str, extensions: list[str]) -> "LocalFile":
1313
"""
1414
Get the file with the given filename and one of the given extensions.
15-
:param dir: The directory to search in.
16-
:param filename: The filename.
17-
:param extensions: The extensions.
18-
:return: The file object.
15+
16+
:param str dir: The directory to search in.
17+
:param str filename: The filename.
18+
:param list[str] extensions: The extensions.
19+
:return LocalFile: The file object.
20+
:raises FileNotFoundError: If no file is found.
1921
"""
2022
for ext in extensions:
2123
path = os.path.join(dir, filename + "." + ext)
@@ -24,6 +26,11 @@ def get_file_matching_extension(cls, dir: str, filename: str, extensions: list[s
2426
raise FileNotFoundError
2527

2628
def __init__(self, path: str):
29+
"""
30+
Initialize the file.
31+
:param str path: The path to the file.
32+
:raises FileNotFoundError: If the file doesn't exist.
33+
"""
2734
if not os.path.exists(path):
2835
raise FileNotFoundError
2936
super().__init__(path)

src/sio3pack/files/remote_file.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
class RemoteFile(File):
55
"""
6-
Base class for all files in a package that are tracked by filetracker.
6+
Base class for a file that is tracked by filetracker.
77
"""
88

99
def __init__(self, path: str):

0 commit comments

Comments
 (0)