diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ecf0202..d4cc765 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -57,3 +57,32 @@ jobs: uses: actions/upload-pages-artifact@v3 with: path: ./_build/html + + build-artifact: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + cache: "pip" + cache-dependency-path: pyproject.toml + + - name: Upgrade pip + run: | + python3 -m pip install --upgrade pip + + - name: Install dependencies + run: python3 -m pip install nox + + - name: Build project + run: nox -s build-project + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: pyos-sphinx-theme-artifact + path: dist/ + retention-days: 7 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28970e2..80e5d74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,12 @@ repos: + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.3 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + - repo: https://github.com/pre-commit/mirrors-prettier rev: 'v4.0.0-alpha.8' hooks: diff --git a/README.md b/README.md index 82315cf..fe11dd4 100755 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ -# pyos-sphinx-theme +# ``pyos-sphinx-theme`` +[![Documentation Status](https://readthedocs.org/projects/pyos-sphinx-theme/badge/?version=latest)](https://pyos-sphinx-theme.readthedocs.io/en/latest/?badge=latest) +[![PyPI version](https://badge.fury.io/py/pyos-sphinx-theme.svg)](https://badge.fury.io/py/pyos-sphinx-theme) [![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-) - -[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) - -A modified version of the [pydata sphinx theme](https:// -github.com/pydata/sphinx-theme) for use with the [pyOpenSci](https://pyopensci.org) project. +A modified version of the [PyData Sphinx Theme](github.com/pydata/sphinx-theme) for use with the [pyOpenSci](https://pyopensci.org) project. ## Contributors diff --git a/docs/conf.py b/docs/conf.py index 527507c..de996b1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,8 @@ +"""pyOpenSci Sphinx Theme configuration.""" from pyos_sphinx_theme import __version__ -project = "Sphinx pyOpenSci Theme" +project = "pyOpenSci Sphinx Theme" copyright = "2024" author = "pyOpenSci" master_doc = "index" @@ -26,7 +27,7 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] html_theme = "pyos_sphinx_theme" -html_title = "pyOpenSci Sphinx Theme" +html_title = project html_copy_source = True html_sourcelink_suffix = "" diff --git a/noxfile.py b/noxfile.py index ec600d0..558d370 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,5 +1,4 @@ -""" -Build and preview this theme documentation locally. +"""Build and preview this theme documentation locally. To build with a live-server: @@ -9,8 +8,9 @@ nox -s docs -- -r """ -import nox + import pathlib + import nox nox.options.reuse_existing_virtualenvs = True @@ -32,7 +32,7 @@ BUILD_PARAMETERS = ["-b", "html"] # Sphinx parameters used to test the build of the guide -TEST_PARAMETERS = ['-W', '--keep-going', '-E', '-a'] +TEST_PARAMETERS = ["-W", "--keep-going", "-E", "-a"] AUTOBUILD_IGNORE = [ "_build", @@ -41,6 +41,7 @@ "tmp", ] + @nox.session def docs(session): """Build the packaging guide.""" @@ -50,18 +51,19 @@ def docs(session): @nox.session(name="docs-live") def docs_live(session): - """ - Build and launch a local copy of the guide. + """Build and launch a local copy of the guide. This session will use sphinx-autobuild to build the guide and launch a local server so you can - browse it locally. It will automatically rebuild the guide when changes are detected in the source. + browse it locally. It will automatically rebuild the guide when changes are + detected in the source. It can be used with the language parameter to build a specific translation, for example: nox -s docs-live -- -D language=es - Note: The docs-live-lang session below is provided as a convenience session for beginner contributors - so they don't need to remember the specific sphinx-build parameters to build a different language. + Note: The docs-live-lang session below is provided as a convenience session for beginner + contributors. so they don't need to remember the specific sphinx-build parameters to + build a different language. """ session.install("-e", ".") cmd = [SPHINX_AUTO_BUILD, *BUILD_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs] @@ -72,12 +74,37 @@ def docs_live(session): # cmd.extend(["--watch", folder]) session.run(*cmd) + @nox.session(name="docs-test") def docs_test(session): - """ - Build the packaging guide with more restricted parameters. + """Build the packaging guide with more restricted parameters. Note: this is the session used in CI/CD to release the guide. """ session.install("-e", ".") - session.run(SPHINX_BUILD, *BUILD_PARAMETERS, *TEST_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs) + session.run( + SPHINX_BUILD, *BUILD_PARAMETERS, *TEST_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs + ) + + +@nox.session(name="linkcheck") +def linkcheck(session): + """Check the links in the documentation.""" + session.install("-e", ".") + session.run( + SPHINX_BUILD, + *BUILD_PARAMETERS, + "-b", + "linkcheck", + SOURCE_DIR, + OUTPUT_DIR, + *session.posargs, + ) + + +@nox.session(name="build-project") +def build_project(session): + """Build the project and create a wheel distribution for pyproject.toml file.""" + session.install("build", "twine") + session.run("python", "-m", "build") + session.run("twine", "check", "dist/*") diff --git a/pyproject.toml b/pyproject.toml index 6dc6608..af255e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,9 +28,6 @@ dependencies = [ ] license = { file = "LICENSE"} -authors = [ - {name = "pyos", email = "hello@2i2c.org"}, -] classifiers = [ "Framework :: Sphinx", "Framework :: Sphinx :: Theme", @@ -59,3 +56,19 @@ docs = [ [project.entry-points."sphinx.html_themes"] "pyos_sphinx_theme" = "pyos_sphinx_theme" + +[tool.ruff] +line-length = 100 +select = [ + "E", # pycodestyle, see https://beta.ruff.rs/docs/rules/#pycodestyle-e-w + "D", # pydocstyle, see https://beta.ruff.rs/docs/rules/#pydocstyle-d + "F", # pyflakes, see https://beta.ruff.rs/docs/rules/#pyflakes-f + "I", # isort, see https://beta.ruff.rs/docs/rules/#isort-i + "N", # pep8-naming, see https://beta.ruff.rs/docs/rules/#pep8-naming-n +] +[tool.ruff.format] +quote-style = "double" + +[tool.ruff.lint.isort] +combine-as-imports = true +force-sort-within-sections = true diff --git a/src/pyos_sphinx_theme/__init__.py b/src/pyos_sphinx_theme/__init__.py index 32ebfd4..dd497c9 100644 --- a/src/pyos_sphinx_theme/__init__.py +++ b/src/pyos_sphinx_theme/__init__.py @@ -1,68 +1,72 @@ """A lightweight theme for pyOpenSci.""" + from pathlib import Path -# from sphinx_book_theme import hash_assets_for_files -from sphinx.util import logging from pydata_sphinx_theme.utils import config_provided_by_user -# disable the video directive and html2dirhtml redirection because they are not implemented yet with this theme -from .video import Video # activate this line to enable the video directive -from .html2dirhtml import redirect_from_html_to_dirhtml # nactivate this line to enable the html2dirhtml redirection +# from sphinx_book_theme import hash_assets_for_files +from sphinx.util import logging __version__ = "0.0.1dev0" LOGGER = logging.getLogger(__name__) THIS_PATH = Path(__file__).parent.resolve() THEME_PATH = THIS_PATH / "theme" / "pyos_sphinx_theme" -LOGO_LIGHT = str((THEME_PATH / "static" / "images" / "logo-light-mode.png").absolute()).replace("\\", "/") -LOGO_DARK = str((THEME_PATH / "static" / "images" / "logo-dark-mode.png").absolute()).replace("\\", "/") +LOGO_LIGHT = str((THEME_PATH / "static" / "images" / "logo-light-mode.png").absolute()).replace( + "\\", "/" +) +LOGO_DARK = str((THEME_PATH / "static" / "images" / "logo-dark-mode.png").absolute()).replace( + "\\", "/" +) + +PYOPENSCI_LOGO_PACKAGE_GUIDE = str( + (THEME_PATH / "static" / "images" / "pyopensci-logo-package-guide.png").absolute() +).replace("\\", "/") +PYOPENSCI_PYTHON_PACKAGE_GUIDE = str( + (THEME_PATH / "static" / "images" / "pyopensci-python-package-guide.png").absolute() +).replace("\\", "/") -PYOPENSCI_LOGO_PACKAGE_GUIDE = str((THEME_PATH / "static" / "images" / "pyopensci-logo-package-guide.png").absolute()).replace("\\", "/") -PYOPENSCI_PYTHON_PACKAGE_GUIDE = str((THEME_PATH / "static" / "images" /"pyopensci-python-package-guide.png").absolute()).replace("\\", "/") def update_config(app): + """Update the Sphinx config with theme-specific options.""" # These are the theme options that will be used in the build theme_options = app.config.html_theme_options # add icons to the navbar if "icon_links" not in theme_options: - - theme_options["icon_links"] =[ - { - "name": "Mastodon", - "url": "https://fosstodon.org/@pyOpenSci", - "icon": "fa-brands fa-mastodon", - }, - ] + theme_options["icon_links"] = [ + { + "name": "Mastodon", + "url": "https://fosstodon.org/@pyOpenSci", + "icon": "fa-brands fa-mastodon", + }, + ] if "external_links" not in theme_options: theme_options["external_links"] = [ - { - "url": "https://www.pyopensci.org", - "name": "pyOpenSci Website", - }, - { - "url": "https://www.pyopensci.org/software-peer-review", - "name": "Peer Review Guide", - }, - { - "url": "https://pyopensci.org/handbook", - "name": "Handbook", - }, - ] - - if not "logo" in theme_options: - theme_options["logo"] = { - "image_dark": LOGO_DARK, - "image_light": LOGO_LIGHT - } - - if not "header_links_before_dropdown" in theme_options: + { + "url": "https://www.pyopensci.org", + "name": "pyOpenSci Website", + }, + { + "url": "https://www.pyopensci.org/software-peer-review", + "name": "Peer Review Guide", + }, + { + "url": "https://pyopensci.org/handbook", + "name": "Handbook", + }, + ] + + if "logo" not in theme_options: + theme_options["logo"] = {"image_dark": LOGO_DARK, "image_light": LOGO_LIGHT} + + if "header_links_before_dropdown" not in theme_options: theme_options["header_links_before_dropdown"] = 4 # Social previews config social_cards = app.config.__dict__.get("ogp_social_cards", {}) - + app.config.ogp_site_name = "pyOpenSci Python Package Guide" app.config.ogp_social_cards = { "line_color": "#6D597A", @@ -84,9 +88,8 @@ def activate_extensions(app, extensions): This also manually triggers the `config-inited` build step to account for added extensions that hook into this event. """ - - # Remove all of the `config-inited` event listeners because they've already triggered - # We'll then re-trigger this event after adding extensions so that *only* their event hooks trigger + # Remove all of the `config-inited` event listeners because they've + # already triggered old_listeners = app.events.listeners["config-inited"] app.events.listeners["config-inited"] = [] @@ -105,14 +108,17 @@ def activate_extensions(app, extensions): def setup(app): + """Initialize Sphinx extension.""" app.add_html_theme("pyos_sphinx_theme", THEME_PATH) app.config.html_favicon = "https://www.pyopensci.org/images/favicon.ico" app.connect("builder-inited", update_config) # app.connect("html-page-context", redirect_from_html_to_dirhtml) # app.add_css_file("static/styles/pyos-sphinx-theme.css") # app.add_js_file("static/scripts/matomo.js") - app.add_css_file("https://fonts.googleapis.com/css2?family=Itim&family=Poppins:wght@400;700&family=Work+Sans:wght@400;700") -# activate_extensions(app, add_extensions) + app.add_css_file( + "https://fonts.googleapis.com/css2?family=Itim&family=Poppins:wght@400;700&family=Work+Sans:wght@400;700" + ) + # activate_extensions(app, add_extensions) # Video directive # app.add_directive("video", Video) diff --git a/src/pyos_sphinx_theme/assets/styles/pyos-sphinx-theme.scss b/src/pyos_sphinx_theme/assets/styles/pyos-sphinx-theme.scss index 1a2367b..61192f0 100644 --- a/src/pyos_sphinx_theme/assets/styles/pyos-sphinx-theme.scss +++ b/src/pyos_sphinx_theme/assets/styles/pyos-sphinx-theme.scss @@ -1,4 +1,3 @@ - @import "pydata_sphinx_theme.css"; // Load theme-specific SCSS @@ -277,6 +276,6 @@ ugly scrollbar to show all the time .bd-header .navbar-nav li a.nav-link:hover { text-decoration: none; text-decoration-thickness: none; - border-bottom: max(3px, .1875rem, .12em) solid var(--pst-color-link-hover); + border-bottom: max(3px, 0.1875rem, 0.12em) solid var(--pst-color-link-hover); text-underline-offset: none; -} \ No newline at end of file +} diff --git a/src/pyos_sphinx_theme/html2dirhtml.py b/src/pyos_sphinx_theme/html2dirhtml.py index 6b46ccc..5dde1f1 100644 --- a/src/pyos_sphinx_theme/html2dirhtml.py +++ b/src/pyos_sphinx_theme/html2dirhtml.py @@ -1,6 +1,9 @@ -from sphinx.util import logging +"""Module to redirect html to dirhtml pages in Sphinx.""" + from pathlib import Path +from sphinx.util import logging + LOGGER = logging.getLogger(__name__) REDIRECT_TEMPLATE = """ @@ -18,10 +21,11 @@ If not, click here to continue. -""" +""" # noqa: E501 + def redirect_from_html_to_dirhtml(app, pagename, templatename, context, doctree): - """If dirhtml builder is used, redirect pagename.html to the directory `pagename`""" + """If dirhtml builder is used, redirect pagename.html to the directory `pagename`.""" if not hasattr(app, "builder"): return if app.builder.name == "dirhtml": diff --git a/src/pyos_sphinx_theme/video.py b/src/pyos_sphinx_theme/video.py index d202cc0..9c62ee4 100644 --- a/src/pyos_sphinx_theme/video.py +++ b/src/pyos_sphinx_theme/video.py @@ -1,12 +1,15 @@ -"""Allows us to embed Google Drive videos into our docs. -""" -from docutils.parsers.rst import Directive +"""Allows us to embed Google Drive videos into our docs.""" + from docutils import nodes +from docutils.parsers.rst import Directive from docutils.parsers.rst.directives import positive_int -IFRAME_TEMPLATE = '' +IFRAME_TEMPLATE = '' # noqa: E501 + class Video(Directive): + """Embed a video into the documentation.""" + arguments = 1 final_argument_whitespace = False has_content = True @@ -14,6 +17,7 @@ class Video(Directive): option_spec = {"width": positive_int, "height": positive_int} def run(self): + """Embed the video into the documentation.""" link = self.content[0] # Video window defaults to a 16:9 ratio width = self.options.get("width", 533) @@ -23,7 +27,9 @@ def run(self): # Link ref: https://drive.google.com/file/d/1vtr54KvM7Vr01wZ1uz0bOrpDvnMGmKy8/view?usp=share_link if "drive.google.com/file" in link: uid = link.split("/d/")[-1].split("/")[0] - iframe = IFRAME_TEMPLATE.format(width=width, height=height, src=f"https://drive.google.com/file/d/{uid}/preview") + iframe = IFRAME_TEMPLATE.format( + width=width, height=height, src=f"https://drive.google.com/file/d/{uid}/preview" + ) # Youtube # Link ref: https://www.youtube.com/watch?v=dQw4w9WgXcQ elif "youtube.com" in link: @@ -31,7 +37,9 @@ def run(self): # In case there were other arguments after the video link uid = uid.split("&")[0] - iframe = IFRAME_TEMPLATE.format(width=width, height=height, src=f"https://www.youtube.com/embed/{uid}") + iframe = IFRAME_TEMPLATE.format( + width=width, height=height, src=f"https://www.youtube.com/embed/{uid}" + ) else: raise ValueError(f"Unidentified video link: {link}")