|
12 | 12 | # |
13 | 13 | import os |
14 | 14 | import sys |
| 15 | +from datetime import date |
| 16 | +from importlib.metadata import PackageNotFoundError, version as pkg_version |
15 | 17 |
|
16 | | -sys.path.insert(0, os.path.abspath("../src/")) |
| 18 | +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
| 19 | +SRC = os.path.join(ROOT, "src") |
| 20 | +sys.path.insert(0, SRC) |
17 | 21 |
|
18 | | -from dependency_injection.version import __version__ # noqa: E402 |
19 | | - |
20 | | - |
21 | | -# -- Auto doc generation ----- |
22 | | - |
23 | | -# # Specify the module to document |
24 | | -# autodoc_mock_imports = ["dependency_injection"] |
25 | 22 |
|
26 | 23 | # The main module |
27 | 24 | root_doc = "index" |
|
30 | 27 | # -- Project information ----------------------------------------------------- |
31 | 28 |
|
32 | 29 | project = "py-dependency-injection" |
33 | | -copyright = "2025, David Runemalm" |
34 | 30 | author = "David Runemalm" |
35 | | - |
36 | | -# Short X.Y version |
37 | | -version = __version__.split("-")[0] |
38 | | - |
39 | | -# Full version string |
40 | | -release = __version__ |
| 31 | +copyright = f"{date.today().year}, {author}" |
| 32 | + |
| 33 | +# Try to read the installed package version; fall back for local builds |
| 34 | +try: |
| 35 | + release = pkg_version("py-dependency-injection") |
| 36 | +except PackageNotFoundError: |
| 37 | + # Fallback: read from source without importing the package |
| 38 | + _ver_file = os.path.join(SRC, "dependency_injection", "version.py") |
| 39 | + _ver = "0.0.0-dev" |
| 40 | + try: |
| 41 | + with open(_ver_file, "r", encoding="utf-8") as f: |
| 42 | + for line in f: |
| 43 | + if line.strip().startswith("__version__"): |
| 44 | + _ver = line.split("=", 1)[1].strip().strip("\"'") |
| 45 | + break |
| 46 | + except OSError: |
| 47 | + pass |
| 48 | + release = _ver |
| 49 | + |
| 50 | +version = release.split("-")[0] |
41 | 51 |
|
42 | 52 |
|
43 | 53 | # -- General configuration --------------------------------------------------- |
|
46 | 56 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
47 | 57 | # ones. |
48 | 58 | extensions = [ |
49 | | - "sphinx.ext.autosummary", |
50 | | - "sphinx.ext.autodoc", |
51 | | - "sphinx.ext.intersphinx", |
| 59 | + "sphinx.ext.autodoc", # generate API from docstrings |
52 | 60 | "sphinx.ext.autosectionlabel", |
53 | | - "sphinx_rtd_theme", |
54 | | - "sphinx.ext.napoleon", |
| 61 | + "sphinx.ext.autosummary", # summary tables + per-object pages |
| 62 | + "sphinx.ext.napoleon", # Google/NumPy docstrings |
| 63 | + "sphinx_autodoc_typehints", # display type hints nicely |
| 64 | + "sphinx.ext.intersphinx", # cross-link to Python stdlib, etc. |
| 65 | + "sphinx.ext.linkcode", # "view source" links |
| 66 | + "sphinx.ext.doctest", # run code in docs if you want |
| 67 | + "sphinx_copybutton", |
| 68 | + "sphinx_inline_tabs", |
55 | 69 | ] |
56 | 70 |
|
| 71 | +# --- Theme --- |
| 72 | +html_theme = "sphinx_rtd_theme" |
| 73 | + |
| 74 | +# Optional RTD theme polish |
| 75 | +# html_theme_options = { |
| 76 | +# "collapse_navigation": True, |
| 77 | +# "navigation_depth": 3, |
| 78 | +# "style_external_links": True, |
| 79 | +# } |
| 80 | + |
| 81 | +html_context = { |
| 82 | + "display_github": False, # Optional: only needed if you're showing GitHub links |
| 83 | + "display_version": True, # ✅ Enable version display manually |
| 84 | + "version": version, # pulled from your __version__ |
| 85 | + "release": release, |
| 86 | +} |
| 87 | + |
| 88 | +html_css_files = ["custom.css"] |
| 89 | + |
| 90 | +# Add any paths that contain custom static files (such as style sheets) here, |
| 91 | +# relative to this directory. They are copied after the builtin static files, |
| 92 | +# so a file named "default.css" will overwrite the builtin "default.css". |
| 93 | +html_static_path = ["_static"] |
| 94 | + |
| 95 | +# --- Autosectionlabel behavior --- |
| 96 | +autosectionlabel_prefix_document = True |
| 97 | + |
| 98 | +# --- Autodoc / Autosummary behavior --- |
57 | 99 | autosummary_generate = True |
| 100 | +# If you re-export public API from __init__.py, uncomment: |
| 101 | +# autosummary_imported_members = True |
58 | 102 | autodoc_default_options = { |
59 | 103 | "members": True, |
60 | | - "undoc-members": True, |
| 104 | + "undoc-members": False, |
| 105 | + "inherited-members": True, |
| 106 | + "show-inheritance": True, |
| 107 | +} |
| 108 | +autodoc_member_order = "bysource" |
| 109 | +autodoc_preserve_defaults = True # show default arg values as written |
| 110 | + |
| 111 | +# sphinx-autodoc-typehints tuning |
| 112 | +typehints_fully_qualified = False |
| 113 | +always_document_param_types = True |
| 114 | +set_type_checking_flag = True |
| 115 | +simplify_optional_unions = True |
| 116 | +typehints_use_rtype = False |
| 117 | + |
| 118 | +# --- Don’t explode when optional deps aren’t installed during docs build --- |
| 119 | +# If your package has optional imports, list them here to mock: |
| 120 | +autodoc_mock_imports = [ |
| 121 | + # "fastapi", "some_heavy_provider", |
| 122 | +] |
| 123 | + |
| 124 | +# --- Copybutton behaviour ------------------------------------------------- |
| 125 | +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d+\]: |Out\[\d+\]: " |
| 126 | +copybutton_prompt_is_regexp = True |
| 127 | + |
| 128 | +# --- Napoleon (Google style recommended) --- |
| 129 | +napoleon_google_docstring = True |
| 130 | +napoleon_numpy_docstring = False |
| 131 | +napoleon_use_param = True |
| 132 | +napoleon_use_rtype = False # keep return type in signature from hints |
| 133 | +napoleon_use_admonition_for_notes = True |
| 134 | +napoleon_use_admonition_for_examples = True |
| 135 | + |
| 136 | +# -- General configuration --------------------------------------------------- |
| 137 | +intersphinx_mapping = { |
| 138 | + "python": ("https://docs.python.org/3", None), |
61 | 139 | } |
62 | 140 |
|
| 141 | +# --- Strictness (great for CI; relax locally if needed) --- |
| 142 | +nitpicky = True # warn on broken references |
| 143 | +nitpick_ignore = [ |
| 144 | + # Example if a third-party type isn’t resolvable: |
| 145 | + # ("py:class", "SomeExternalType"), |
| 146 | +] |
| 147 | +# In CI, also run sphinx-build with -W to turn warnings into errors. |
| 148 | + |
63 | 149 | # Add any paths that contain templates here, relative to this directory. |
64 | 150 | templates_path = ["_templates"] |
65 | 151 |
|
|
84 | 170 | # A list of ignored prefixes for module index sorting. |
85 | 171 | # modindex_common_prefix = [] |
86 | 172 |
|
87 | | -autodoc_member_order = "alphabetical" |
88 | | - |
89 | | -# -- Options for HTML output ------------------------------------------------- |
90 | 173 |
|
91 | | -# The theme to use for HTML and HTML Help pages. See the documentation for |
92 | | -# a list of builtin themes. |
93 | | -# |
94 | | -html_theme = "sphinx_rtd_theme" |
95 | | - |
96 | | -html_context = { |
97 | | - "display_github": False, # Optional: only needed if you're showing GitHub links |
98 | | - "display_version": True, # ✅ Enable version display manually |
99 | | - "version": version, # pulled from your __version__ |
100 | | - "release": release, |
101 | | -} |
102 | | - |
103 | | -html_css_files = ["custom.css"] |
104 | | - |
105 | | -# Add any paths that contain custom static files (such as style sheets) here, |
106 | | -# relative to this directory. They are copied after the builtin static files, |
107 | | -# so a file named "default.css" will overwrite the builtin "default.css". |
108 | | -html_static_path = ["_static"] |
109 | | - |
110 | | -intersphinx_mapping = { |
111 | | - "python": ("https://docs.python.org/3", None), |
112 | | - "sqlalchemy": ("http://docs.sqlalchemy.org/en/latest/", None), |
113 | | -} |
| 174 | +# -- Linkcode: map objects to GitHub ----------------------------------------- |
| 175 | +def linkcode_resolve(domain, info): |
| 176 | + if domain != "py" or not info.get("module"): |
| 177 | + return None |
| 178 | + import importlib |
| 179 | + import pathlib |
| 180 | + import inspect as _inspect |
| 181 | + |
| 182 | + try: |
| 183 | + mod = importlib.import_module(info["module"]) |
| 184 | + except Exception: |
| 185 | + return None |
| 186 | + |
| 187 | + obj = mod |
| 188 | + for part in info["fullname"].split("."): |
| 189 | + obj = getattr(obj, part, None) |
| 190 | + if obj is None: |
| 191 | + return None |
| 192 | + |
| 193 | + try: |
| 194 | + fn = _inspect.getsourcefile(obj) or _inspect.getfile(obj) |
| 195 | + source, lineno = _inspect.getsourcelines(obj) |
| 196 | + except Exception: |
| 197 | + return None |
| 198 | + |
| 199 | + fn = pathlib.Path(fn).resolve() |
| 200 | + try: |
| 201 | + relpath = fn.relative_to(SRC) |
| 202 | + except ValueError: |
| 203 | + return None |
| 204 | + |
| 205 | + # Prefer commit/tag if present; fall back to branch-ish |
| 206 | + ref = ( |
| 207 | + os.environ.get("READTHEDOCS_GIT_IDENTIFIER") # exact commit or tag |
| 208 | + or os.environ.get("READTHEDOCS_VERSION") # 'latest'/'stable'/branch |
| 209 | + or "master" |
| 210 | + ) |
| 211 | + |
| 212 | + start, end = lineno, lineno + len(source) - 1 |
| 213 | + return ( |
| 214 | + "https://github.com/runemalm/py-dependency-injection/" |
| 215 | + f"blob/{ref}/src/{relpath}#L{start}-L{end}" |
| 216 | + ) |
0 commit comments