From e30b72dd2a82200c7cbd27743c89f497e219975c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 23 Dec 2022 09:46:09 -0600 Subject: [PATCH] Add spelling and docstring enforcement (#280) --- .github/workflows/tests.yml | 22 +++++++++++++++++++--- .pre-commit-config.yaml | 12 ++++++------ oct2py/_version.py | 4 +--- oct2py/core.py | 1 + oct2py/demo.py | 1 + oct2py/dynamic.py | 15 ++++++++++++++- oct2py/io.py | 12 ++++++++++-- oct2py/speed_check.py | 1 + oct2py/tests/test_numpy.py | 15 ++++++++++----- oct2py/thread_check.py | 1 + oct2py/utils.py | 1 + pyproject.toml | 31 +++++++++++++++++++++---------- 12 files changed, 86 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2c68606d..64f46abb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,11 +46,11 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} docs: - runs-on: ubuntu-latest + runs-on: windows-latest steps: - uses: actions/checkout@v3 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - run: pip install hatch; hatch run docs:build + - run: hatch run docs:build test_lint: name: Test Lint @@ -62,7 +62,7 @@ jobs: run: | hatch run typing:test hatch run lint:style - pipx run 'validate-pyproject[all]' pyproject.toml + pipx run interrogate -v . pipx run doc8 --max-line-length=200 docs/source *.md check_links: @@ -72,3 +72,19 @@ jobs: - uses: actions/checkout@v3 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1 + + + tests_check: # This job does nothing and is only used for the branch protection + if: always() + needs: + - build + - test_lint + - docs + - check_links + - check_release + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bdaad897..3bfcb797 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,17 +5,17 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - - id: end-of-file-fixer - id: check-case-conflict + - id: check-ast + - id: check-docstring-first - id: check-executables-have-shebangs - - id: requirements-txt-fixer - id: check-added-large-files - id: check-case-conflict + - id: check-merge-conflict + - id: check-json - id: check-toml - id: check-yaml - - id: debug-statements - - id: forbid-new-submodules - - id: check-builtin-literals + - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema @@ -36,7 +36,7 @@ repos: - id: black - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.165 + rev: v0.0.189 hooks: - id: ruff args: ["--fix"] diff --git a/oct2py/_version.py b/oct2py/_version.py index 85877a65..fc562834 100644 --- a/oct2py/_version.py +++ b/oct2py/_version.py @@ -1,6 +1,4 @@ -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - +"""Version info.""" import re from collections import namedtuple from typing import List diff --git a/oct2py/core.py b/oct2py/core.py index fe3eff18..4c23aba8 100644 --- a/oct2py/core.py +++ b/oct2py/core.py @@ -1,3 +1,4 @@ +"""Core oct2py functionality.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. diff --git a/oct2py/demo.py b/oct2py/demo.py index d3363ad3..128ab76c 100644 --- a/oct2py/demo.py +++ b/oct2py/demo.py @@ -1,3 +1,4 @@ +"""oct2py demo.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. diff --git a/oct2py/dynamic.py b/oct2py/dynamic.py index 2139e073..d44736a2 100644 --- a/oct2py/dynamic.py +++ b/oct2py/dynamic.py @@ -1,3 +1,4 @@ +"""dynamic value handling.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. @@ -22,6 +23,7 @@ class OctavePtr: """A pointer to an Octave workspace value.""" def __init__(self, session_weakref, name, address): + """Initialize the pointer.""" self._name = name self._address = address self._ref = session_weakref @@ -43,6 +45,7 @@ class _DocDescriptor: """ def __init__(self, session_weakref, name): + """Initialize the descriptor.""" self.ref = session_weakref self.name = name self.doc = None @@ -74,10 +77,12 @@ class OctaveFunctionPtr(OctavePtr): """An object that acts as a pointer to an Octave function.""" def __init__(self, session_weakref, name): + """Initialize the pointer.""" address = "@%s" % name super().__init__(session_weakref, name, address) def __call__(self, *inputs, **kwargs): + """Call the function.""" # Check for allowed keyword arguments allowed = [ "verbose", @@ -106,6 +111,7 @@ def __call__(self, *inputs, **kwargs): return self._ref().feval(self.name, *inputs, **kwargs) def __repr__(self): + """A string repr of the pointer.""" return '"%s" Octave function' % self.name @@ -113,12 +119,14 @@ class OctaveUserClassAttr(OctavePtr): """An attribute associated with an Octave user class instance.""" def __get__(self, instance, owner=None): + """Get a method or property on the class.""" if instance is None: return "dynamic attribute" pointer = OctaveUserClass.to_pointer(instance) return instance._ref().feval("get", pointer, self.address) def __set__(self, instance, value): + """Set a method or property on the class.""" if instance is None: return pointer = OctaveUserClass.to_pointer(instance) @@ -132,12 +140,14 @@ class _MethodDocDescriptor: """ def __init__(self, session_weakref, class_name, name): + """Initialize the descriptor.""" self.ref = session_weakref self.class_name = class_name self.name = name self.doc = None def __get__(self, instance, owner=None): + """Get the documentation.""" if self.doc is not None: return self.doc session = self.ref() @@ -152,19 +162,22 @@ class OctaveUserClassMethod(OctaveFunctionPtr): """A method for a user defined Octave class.""" def __init__(self, session_weakref, name, class_name): + """Initialize the pointer.""" OctaveFunctionPtr.__init__(self, session_weakref, name) self.class_name = class_name def __get__(self, instance, owner=None): - # Bind to the instance. + """Bind to the instance.""" return types.MethodType(self, instance) def __call__(self, instance: "OctaveUserClass", *inputs: Any, **kwargs: Any) -> Any: + """Call the class method.""" pointer = OctaveUserClass.to_pointer(instance) inputs = (pointer,) + inputs self._ref().feval(self.name, *inputs, **kwargs) def __repr__(self): + """Str repr of the pointer.""" return f'"{self.name}" Octave method for object' diff --git a/oct2py/io.py b/oct2py/io.py index bac9f31c..ca0fa6d1 100644 --- a/oct2py/io.py +++ b/oct2py/io.py @@ -1,3 +1,4 @@ +"""io handling.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. @@ -24,9 +25,13 @@ except Exception: class Series: # type:ignore + """placeholder.""" + pass class DataFrame: # type:ignore + """placeholder.""" + pass @@ -81,7 +86,7 @@ class Struct(dict): """ def __getattr__(self, attr): - # Access the dictionary keys for unknown attributes. + """Access the dictionary keys for unknown attributes.""" try: return self[attr] except KeyError: @@ -89,7 +94,7 @@ def __getattr__(self, attr): raise AttributeError(msg) from None def __getitem__(self, attr): - # Get a dict value; create a Struct if requesting a Struct member. + """Get a dict value; create a Struct if requesting a Struct member.""" # Do not create a key if the attribute starts with an underscore. if attr in self.keys() or attr.startswith("_"): return dict.__getitem__(self, attr) @@ -183,6 +188,7 @@ def __getitem__(self, item): return item def __repr__(self): + """A str repr for the struct array.""" shape = self.shape if len(shape) == 1: shape = (shape[0], 1) @@ -234,6 +240,7 @@ def __new__(cls, value, session=None): return obj def __repr__(self): + """A string repr for the cell array.""" shape = self.shape if len(shape) == 1: shape = (shape[0], 1) @@ -242,6 +249,7 @@ def __repr__(self): return msg.replace(", dtype=object", "", 1) def __getitem__(self, key): + """Get an element of the array.""" if key == 0 and self.size == 1: # Note: # Can't use `return super().ravel()[0]` here diff --git a/oct2py/speed_check.py b/oct2py/speed_check.py index deb7482d..68a5ac48 100644 --- a/oct2py/speed_check.py +++ b/oct2py/speed_check.py @@ -1,3 +1,4 @@ +"""oct2py speed check.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. diff --git a/oct2py/tests/test_numpy.py b/oct2py/tests/test_numpy.py index 9eb1b37c..330ebd2c 100644 --- a/oct2py/tests/test_numpy.py +++ b/oct2py/tests/test_numpy.py @@ -1,4 +1,5 @@ import os +import warnings import numpy as np @@ -27,10 +28,12 @@ def test_scalars(self): outgoing = np.random.randint(-255, 255) + np.random.rand(1) if typecode in "US": outgoing = np.array("spam").astype(typecode) - try: - outgoing = outgoing.astype(typecode) - except TypeError: - continue + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + try: + outgoing = outgoing.astype(typecode) + except TypeError: + continue incoming = self.oc.roundtrip(outgoing) try: assert np.allclose(incoming, outgoing) @@ -60,7 +63,9 @@ def test_ndarrays(self): outgoing = outgoing.astype(typecode) except TypeError: continue - incoming = self.oc.roundtrip(outgoing) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + incoming = self.oc.roundtrip(outgoing) incoming = np.array(incoming) if outgoing.size == 1: outgoing = outgoing.squeeze() diff --git a/oct2py/thread_check.py b/oct2py/thread_check.py index 88e1ab37..99d8fbe1 100644 --- a/oct2py/thread_check.py +++ b/oct2py/thread_check.py @@ -1,3 +1,4 @@ +"""oct2py thread check.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. diff --git a/oct2py/utils.py b/oct2py/utils.py index 8faaa363..ba82707f 100644 --- a/oct2py/utils.py +++ b/oct2py/utils.py @@ -1,3 +1,4 @@ +"""oct2py general utils.""" # Copyright (c) oct2py developers. # Distributed under the terms of the MIT License. diff --git a/pyproject.toml b/pyproject.toml index f2f598e1..7fcc707c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,15 +43,9 @@ docs = [ "sphinx", "sphinx-bootstrap-theme", "myst_parser", - "sphinx_rtd_theme" + "sphinx_rtd_theme", + "sphinxcontrib_spelling", ] -lint = [ - "black[jupyter]>=22.6.0", - "mdformat>0.7", - "mdformat-gfm>=0.3.5", - "ruff>=0.0.156" -] -typing = ["mypy>=0.990"] [tool.jupyter-releaser.hooks] before-build-python = ["sudo apt-get update", "sudo apt-get install -qq octave octave-signal liboctave-dev"] @@ -80,12 +74,19 @@ ARGS = "--doctest-modules -l --cov-report html --cov-report=xml --cov=oct2py -vv test = "python -m pytest $ARGS --cov-fail-under 90 {args}" [tool.hatch.envs.typing] -features = ["typing", "test"] +features = ["test"] +dependencies = ["mypy>=0.990"] [tool.hatch.envs.typing.scripts] test = "mypy --install-types --non-interactive {args:oct2py}" [tool.hatch.envs.lint] -features = ["lint"] +dependencies = [ + "black[jupyter]==22.10.0", + "mdformat>0.7", + "mdformat-gfm>=0.3.5", + "ruff==0.0.189" +] +detached = true [tool.hatch.envs.lint.scripts] style = [ "ruff {args:.}", @@ -164,3 +165,13 @@ ignore = [ # N806 Variable `V` in function should be lowercase "oct2py/tests/*" = ["S101", "N806"] "oct2py/ipython/tests/*" = ["S101", "N806"] + +[tool.interrogate] +ignore-init-module=true +ignore-private=true +ignore-semiprivate=true +ignore-property-decorators=true +ignore-nested-functions=true +ignore-nested-classes=true +fail-under=100 +exclude = ["*/tests", "docs"]