diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml new file mode 100644 index 0000000..5fe73e9 --- /dev/null +++ b/.github/workflows/sphinx.yml @@ -0,0 +1,54 @@ +name: GitHub Pages + +on: + push: + branches: + - main + pull_request: + +jobs: + deploy: + runs-on: ubuntu-20.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v3 + with: + python-version: '3.8' + + - name: Upgrade pip + run: | + # install pip=>20.1 to use "pip cache dir" + python3 -m pip install --upgrade pip + python3 -m pip install . + + - name: Get pip cache dir + id: pip-cache + run: echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: python3 -m pip install -r ./docs/requirements.txt + + - run: touch .nojekyll + + - run: | + touch ./docs/.nojekyll + cd docs && make html + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/_build/html diff --git a/.gitignore b/.gitignore index 42db774..9225177 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ pip-wheel-metadata # editors .vscode + +docs/_build diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..92dd33a --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..4d20d14 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx +sphinx_autodoc_typehints +pydata-sphinx-theme diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..2378e33 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,59 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = "pysen" +copyright = "2022, Preferred Networks, inc." +author = "Preferred Networks, inc." + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.napoleon", + "sphinx.ext.autodoc", + "sphinx_autodoc_typehints", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "venv"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "pydata_sphinx_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + + +# sphinx.ext.autodoc +autodoc_typehints = "description" diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..a0a35a5 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,35 @@ +.. pysen documentation master file, created by + sphinx-quickstart on Mon Apr 25 09:37:35 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +Tutorials +========= + +.. toctree:: + :maxdepth: 1 + + overview + + +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + reference/ext/index + reference/factory + reference/mypy + reference/py_version + reference/pyproject_model + reference/source + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/overview.rst b/docs/source/overview.rst new file mode 100644 index 0000000..38837cf --- /dev/null +++ b/docs/source/overview.rst @@ -0,0 +1,44 @@ +.. _Overview: + +How to configure pysen +====================== + +Overview +-------- + +.. code-block:: toml + + [tool.pysen] + version = "0.10" + builder = "lint.py" + + [tool.pysen.lint] + enable_black = true + enable_flake8 = true + enable_isort = true + enable_mypy = true + mypy_preset = "strict" + line_length = 88 + py_version = "py37" + + [[tool.pysen.lint.mypy_targets]] + paths = ["."] + + +- ``tool.pysen`` corresponds to :class:`pysen.pyproject_model.Config` + - For example, ``builder`` under ``[tool.pysen]`` passes to :class:`pysen.pyproject_model.Config` + +- ``tool.pysen.lint`` corresponds to :class:`pysen.pyproject_model.LintConfig` + - If you specify ``line_length`` to ``88``, it is passed to :class:`pysen.pyproject_model.LintConfig` + +- ``tool.pysen.plugin``, corresponds to :class:`pysen.pyproject_model.PluginConfig` + +You can know what you can change a configuration by checking :class:`~pysen.pyproject_model.Config`, +:class:`~pysen.pyproject_model.LintConfig`, and :class:`~pysen.pyproject_model.PluginConfig`. + +If a entry is not an instance of Python built-in class, it cannot be directly specified. +For example, ``mypy_targets`` corresponds to :class:`pysen.ext.mypy_wrapper.MypyTarget`, which is not a Python built-in (or equivarent to built-in) class. +To configure such option, you need to create a section ``[[tool.pysen.lint.mypy_targets]]``. +:class:`~pysen.ext.mypy_wrapper.MypyPlugin` has parameters ``paths`` and ``namespace_packages``. +Since ``paths`` is equivarent to ``str`` and ``namespace_packages`` is ``bool``, you can set them in the section. +Note that ``namespace_packages`` is omitted since it is :obj:`False` by default. diff --git a/docs/source/reference/ext/black.rst b/docs/source/reference/ext/black.rst new file mode 100644 index 0000000..4fab17e --- /dev/null +++ b/docs/source/reference/ext/black.rst @@ -0,0 +1,7 @@ +.. module:: pysen.ext.black_wrapper + +black +===== + + +.. autoclass:: BlackSetting diff --git a/docs/source/reference/ext/flake8.rst b/docs/source/reference/ext/flake8.rst new file mode 100644 index 0000000..3c07469 --- /dev/null +++ b/docs/source/reference/ext/flake8.rst @@ -0,0 +1,6 @@ +.. module:: pysen.ext.flake8_wrapper + +flake8 +====== + +.. autoclass:: Flake8Setting diff --git a/docs/source/reference/ext/index.rst b/docs/source/reference/ext/index.rst new file mode 100644 index 0000000..a0d2e0b --- /dev/null +++ b/docs/source/reference/ext/index.rst @@ -0,0 +1,11 @@ +pysen.ext +========= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + black + flake8 + isort + mypy diff --git a/docs/source/reference/ext/isort.rst b/docs/source/reference/ext/isort.rst new file mode 100644 index 0000000..11935dc --- /dev/null +++ b/docs/source/reference/ext/isort.rst @@ -0,0 +1,10 @@ +.. module:: pysen.ext.isort_wrapper + +isort +===== + +.. autoclass:: IsortSetting + +.. autoclass:: IsortSectionName + :members: + :undoc-members: diff --git a/docs/source/reference/ext/mypy.rst b/docs/source/reference/ext/mypy.rst new file mode 100644 index 0000000..cbcf4c8 --- /dev/null +++ b/docs/source/reference/ext/mypy.rst @@ -0,0 +1,10 @@ +.. module:: pysen.ext.mypy_wrapper + +mypy +==== + +.. autoclass:: MypySetting + +.. autoclass:: MypyPlugin + +.. autoclass:: MypyTarget diff --git a/docs/source/reference/factory.rst b/docs/source/reference/factory.rst new file mode 100644 index 0000000..ee3a3f7 --- /dev/null +++ b/docs/source/reference/factory.rst @@ -0,0 +1,6 @@ +.. module:: pysen.factory + +pysen.factory +============= + +.. autoclass:: MypyModuleOption diff --git a/docs/source/reference/mypy.rst b/docs/source/reference/mypy.rst new file mode 100644 index 0000000..fa3c0d6 --- /dev/null +++ b/docs/source/reference/mypy.rst @@ -0,0 +1,6 @@ +.. module:: pysen.mypy + +pysen.mypy +========== + +.. autoclass:: MypyPreset diff --git a/docs/source/reference/py_version.rst b/docs/source/reference/py_version.rst new file mode 100644 index 0000000..af01696 --- /dev/null +++ b/docs/source/reference/py_version.rst @@ -0,0 +1,8 @@ +.. module:: pysen.py_version + +pysen.py_version +================ + +.. autoclass:: VersionRepresentation + +.. autoclass:: PythonVersion diff --git a/docs/source/reference/pyproject_model.rst b/docs/source/reference/pyproject_model.rst new file mode 100644 index 0000000..2159e47 --- /dev/null +++ b/docs/source/reference/pyproject_model.rst @@ -0,0 +1,15 @@ +.. module:: pysen.pyproject_model + +pysen.pyproject_model +===================== + + +.. autoclass:: pysen.pyproject_model.Config + +:class:`~pysen.pyproject_model.LintConfig` + +.. autoclass:: pysen.pyproject_model.LintConfig + +:class:`~pysen.pyproject_model.PluginConfig` + +.. autoclass:: pysen.pyproject_model.PluginConfig diff --git a/docs/source/reference/source.rst b/docs/source/reference/source.rst new file mode 100644 index 0000000..1faebb7 --- /dev/null +++ b/docs/source/reference/source.rst @@ -0,0 +1,6 @@ +.. module:: pysen.source + +pysen.source +============ + +.. autoclass:: Source diff --git a/pysen/factory.py b/pysen/factory.py index f8de1e5..fb12769 100644 --- a/pysen/factory.py +++ b/pysen/factory.py @@ -1,6 +1,8 @@ import dataclasses import pathlib -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional + +import dacite from .black import Black, BlackSetting from .component import ComponentBase @@ -18,6 +20,16 @@ from .source import Source +def _parse_python_version(s: Any) -> PythonVersion: + if not isinstance(s, str): + raise dacite.WrongTypeError(str, s) + + try: + return PythonVersion.parse_short_representation(s) + except KeyError as e: + raise dacite.DaciteError(str(e)) + + @dataclasses.dataclass class MypyModuleOption: preset: Optional[MypyPreset] = None @@ -51,7 +63,7 @@ class ConfigureLintOptions: mypy_modules: Optional[Dict[str, MypyModuleOption]] = None source: Optional[Source] = None line_length: Optional[int] = None - py_version: Optional[PythonVersion] = None + py_version: Optional[str] = None isort_known_third_party: Optional[List[str]] = None isort_known_first_party: Optional[List[str]] = None isort_default_section: Optional[IsortSectionName] = None @@ -65,7 +77,7 @@ def configure_lint(options: ConfigureLintOptions) -> List[ComponentBase]: python_version: PythonVersion if options.py_version is not None: - python_version = options.py_version + python_version = _parse_python_version(options.py_version) else: python_version = PythonVersion(3, 7) diff --git a/pysen/pyproject_model.py b/pysen/pyproject_model.py index 0772480..09f2607 100644 --- a/pysen/pyproject_model.py +++ b/pysen/pyproject_model.py @@ -53,7 +53,7 @@ class PluginConfig: @dataclasses.dataclass class Config: - version: Optional[VersionRepresentation] = None + version: Optional[str] = None lint: Optional[LintConfig] = None builder: Optional[pathlib.Path] = None plugin: Optional[List[PluginConfig]] = None diff --git a/tests/test_pyproject_model.py b/tests/test_pyproject_model.py index f080652..0e7a7d5 100644 --- a/tests/test_pyproject_model.py +++ b/tests/test_pyproject_model.py @@ -291,7 +291,7 @@ def test_example() -> None: assert lint.enable_mypy assert lint.line_length == 88 assert isinstance(lint.line_length, int) - assert lint.py_version == PythonVersion(3, 7) + assert lint.py_version == "py37" assert lint.isort_known_first_party == ["alpha"] assert lint.isort_known_third_party == ["beta", "gamma"] assert lint.isort_default_section == IsortSectionName.THIRDPARTY @@ -337,7 +337,7 @@ def test_simple_source() -> None: lint = config.lint assert lint.enable_mypy assert lint.line_length == 80 - assert lint.py_version == PythonVersion(2, 7) + assert lint.py_version == "py27" assert lint.source is not None source = lint.source assert source.includes == { @@ -395,7 +395,7 @@ def test_lint_config_update() -> None: enable_black=False, enable_flake8=True, isort_known_first_party=["foo"], - py_version=PythonVersion(3, 8), + py_version="py38", ) lhs.update(rhs) @@ -408,7 +408,7 @@ def test_lint_config_update() -> None: assert lhs.line_length == 80 assert lhs.isort_known_first_party == ["foo"] assert lhs.isort_known_third_party == ["piyo"] - assert lhs.py_version == PythonVersion(3, 8) + assert lhs.py_version == "py38" def test_has_tool_section() -> None: