Skip to content

Commit

Permalink
Merge pull request #109 from lsst/tickets/DM-32801
Browse files Browse the repository at this point in the history
DM-32801: Add sphinx docs build action
  • Loading branch information
timj committed Dec 4, 2021
2 parents e1fd236 + 9ba4fc8 commit 7409401
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 39 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/build_docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: docs

on:
push:
branches:
- main
pull_request:

jobs:
build_sphinx_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Update pip/wheel infrastructure
run: |
python -m pip install --upgrade pip
pip install wheel
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Build and install
run: pip install -v .

- name: Install documenteer
run: pip install 'documenteer[pipelines]<0.7'

- name: Build documentation
working-directory: ./doc
run: package-docs build
24 changes: 24 additions & 0 deletions .github/workflows/docstyle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: lint docstrings

on:
push:
branches:
- main
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install
run: pip install pydocstyle

- name: Run linter
run: pydocstyle python/lsst/
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
html_title = project
html_short_title = project
doxylink = {}
exclude_patterns = ["changes/*"]
1 change: 1 addition & 0 deletions python/lsst/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#
# Use of this source code is governed by a 3-clause BSD-style
# license that can be found in the LICENSE file.
"""Top-level LSST package definition."""

import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
1 change: 1 addition & 0 deletions python/lsst/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#
# Use of this source code is governed by a 3-clause BSD-style
# license that can be found in the LICENSE file.
"""General LSST Utilities."""

from .get_caller_name import *
from .doImport import *
Expand Down
1 change: 1 addition & 0 deletions python/lsst/utils/_forwarded.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@

@deprecated(reason=_REASON, version=_VERSION_REMOVED, category=FutureWarning)
def demangleType(type_name: str) -> str:
"""Demangle a C++ type string."""
import lsst.cpputils
return lsst.cpputils.demangleType(type_name)
1 change: 1 addition & 0 deletions python/lsst/utils/backtrace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#
# Use of this source code is governed by a 3-clause BSD-style
# license that can be found in the LICENSE file.
"""Temporary forwarding of backtrace to cpputils package."""

__all__ = ["isEnabled"]

Expand Down
5 changes: 2 additions & 3 deletions python/lsst/utils/inheritDoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


def inheritDoc(klass: Type) -> Callable:
"""Extends existing documentation for a method that exists in another
"""Extend existing documentation for a method that exists in another
class and extend it with any additional documentation defined.
This decorator takes a class from which to draw documentation from as an
Expand All @@ -34,8 +34,7 @@ class and extend it with any additional documentation defined.
Intermediate decorator used in the documentation process.
"""
def tmpDecorator(method: Type) -> Callable:
"""Decorator to update the documentation from a class with the same
method.
"""Update the documentation from a class with the same method.
"""
methodName = method.__name__
if not hasattr(klass, methodName):
Expand Down
23 changes: 10 additions & 13 deletions python/lsst/utils/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@


def trace_set_at(name: str, number: int) -> None:
"""Adjusts logging level to display messages with the trace number being
"""Adjust logging level to display messages with the trace number being
less than or equal to the provided value.
Parameters
Expand All @@ -51,16 +51,8 @@ def trace_set_at(name: str, number: int) -> None:
number : `int`
The trace number threshold for display.
Notes
-----
Loggers ``TRACE0.`` to ``TRACE5.`` are set. All loggers above
the specified threshold are set to ``INFO`` and those below the threshold
are set to ``DEBUG``. The expectation is that ``TRACE`` loggers only
issue ``DEBUG`` log messages.
Examples
--------
.. code-block:: python
lsst.utils.logging.trace_set_at("lsst.afw", 3)
Expand All @@ -70,6 +62,11 @@ def trace_set_at(name: str, number: int) -> None:
Notes
-----
Loggers ``TRACE0.`` to ``TRACE5.`` are set. All loggers above
the specified threshold are set to ``INFO`` and those below the threshold
are set to ``DEBUG``. The expectation is that ``TRACE`` loggers only
issue ``DEBUG`` log messages.
If ``lsst.log`` is installed, this function will also call
`lsst.log.utils.traceSetAt` to ensure that non-Python loggers are
also configured correctly.
Expand All @@ -85,14 +82,14 @@ def trace_set_at(name: str, number: int) -> None:


class _F:
"""
Format, supporting `str.format()` syntax.
"""Format, supporting `str.format()` syntax.
Notes
-----
This follows the recommendation from
https://docs.python.org/3/howto/logging-cookbook.html#using-custom-message-objects
"""

def __init__(self, fmt: str, /, *args: Any, **kwargs: Any):
self.fmt = fmt
self.args = args
Expand Down Expand Up @@ -135,7 +132,7 @@ class LsstLogAdapter(LoggerAdapter):

@contextmanager
def temporary_log_level(self, level: Union[int, str]) -> Generator:
"""A context manager that temporarily sets the level of this logger.
"""Temporarily set the level of this logger.
Parameters
----------
Expand All @@ -151,7 +148,7 @@ def temporary_log_level(self, level: Union[int, str]) -> Generator:

@property
def level(self) -> int:
"""Current level of this logger (``int``)."""
"""Return current level of this logger (``int``)."""
return self.logger.level

def getChild(self, name: str) -> LsstLogAdapter:
Expand Down
49 changes: 27 additions & 22 deletions python/lsst/utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ def sort_tests(tests) -> unittest.TestSuite:
A combined `~unittest.TestSuite` with
`~lsst.utils.tests.MemoryTestCase` at the end.
"""

suite = unittest.TestSuite()
memtests = []
for test_suite in tests:
Expand Down Expand Up @@ -157,20 +156,22 @@ class ExecutablesTestCase(unittest.TestCase):
must subclass this class in their own test file and invoke
the create_executable_tests() class method to register the tests.
"""

TESTS_DISCOVERED = -1

@classmethod
def setUpClass(cls) -> None:
"""Abort testing if automated test creation was enabled and
no tests were found."""

no tests were found.
"""
if cls.TESTS_DISCOVERED == 0:
raise RuntimeError("No executables discovered.")

def testSanity(self) -> None:
"""This test exists to ensure that there is at least one test to be
"""Ensure that there is at least one test to be
executed. This allows the test runner to trigger the class set up
machinery to test whether there are some executables to test."""
machinery to test whether there are some executables to test.
"""
pass

def assertExecutable(self, executable: str, root_dir: Optional[str] = None,
Expand Down Expand Up @@ -198,7 +199,6 @@ def assertExecutable(self, executable: str, root_dir: Optional[str] = None,
AssertionError
The executable did not return 0 exit status.
"""

if root_dir is not None and not os.path.isabs(executable):
executable = os.path.join(root_dir, executable)

Expand Down Expand Up @@ -287,7 +287,6 @@ def create_executable_tests(cls, ref_file: str, executables: Optional[Sequence[s
--------
>>> cls.create_executable_tests(__file__)
"""

# Get the search directory from the reference file
ref_dir = os.path.abspath(os.path.dirname(ref_file))

Expand Down Expand Up @@ -329,7 +328,6 @@ def getTempFilePath(ext: str, expectOutput: bool = True) -> Iterator[str]:
Parameters
----------
ext : `str`
File name extension, e.g. ``.fits``.
expectOutput : `bool`, optional
Expand All @@ -339,13 +337,13 @@ def getTempFilePath(ext: str, expectOutput: bool = True) -> Iterator[str]:
Returns
-------
`str`
path : `str`
Path for a temporary file. The path is a combination of the caller's
file path and the name of the top-level function
Notes
-----
::
Examples
--------
.. code-block:: python
# file tests/testFoo.py
import unittest
Expand Down Expand Up @@ -423,15 +421,15 @@ class TestCase(unittest.TestCase):


def inTestCase(func: Callable) -> Callable:
"""A decorator to add a free function to our custom TestCase class, while
"""Add a free function to our custom TestCase class, while
also making it available as a free function.
"""
setattr(TestCase, func.__name__, func)
return func


def debugger(*exceptions):
"""Decorator to enter the debugger when there's an uncaught exception
"""Enter the debugger when there's an uncaught exception
To use, just slap a ``@debugger()`` on your function.
Expand Down Expand Up @@ -823,7 +821,7 @@ def decorator(cls: Type) -> None:


def methodParameters(**settings: Sequence[Any]) -> Callable:
"""Method decorator for unit tests
"""Iterate over supplied settings to create subtests automatically.
This decorator iterates over the supplied settings, using
``TestCase.subTest`` to communicate the values in the event of a failure.
Expand All @@ -836,13 +834,15 @@ def methodParameters(**settings: Sequence[Any]) -> Callable:
Examples
--------
::
.. code-block:: python
@methodParameters(foo=[1, 2], bar=[3, 4])
def testSomething(self, foo, bar):
...
will run::
will run:
.. code-block:: python
testSomething(foo=1, bar=3)
testSomething(foo=2, bar=4)
Expand Down Expand Up @@ -872,12 +872,15 @@ def _cartesianProduct(settings: Mapping[str, Sequence[Any]]) -> Mapping[str, Seq
Parameter combinations covering the cartesian product (all possible
combinations) of the input parameters.
Example
-------
Examples
--------
.. code-block:: python
cartesianProduct({"foo": [1, 2], "bar": ["black", "white"]})
returns:
will return:
.. code-block:: python
{"foo": [1, 1, 2, 2], "bar": ["black", "white", "black", "white"]}
"""
Expand All @@ -902,14 +905,16 @@ def classParametersProduct(**settings: Sequence[Any]) -> Callable:
Examples
--------
::
.. code-block:: python
@classParametersProduct(foo=[1, 2], bar=[3, 4])
class MyTestCase(unittest.TestCase):
...
will generate four classes, as if you wrote::
.. code-block:: python
class MyTestCase_1_3(unittest.TestCase):
foo = 1
bar = 3
Expand All @@ -936,7 +941,7 @@ class MyTestCase_2_4(unittest.TestCase):


def methodParametersProduct(**settings: Sequence[Any]) -> Callable:
"""Method decorator for unit tests
"""Iterate over cartesian product creating sub tests.
This decorator iterates over the cartesian product of the supplied
settings, using `~unittest.TestCase.subTest` to communicate the values in
Expand Down
2 changes: 1 addition & 1 deletion python/lsst/utils/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def logInfo(obj: Any, prefix: str, logLevel: int = logging.DEBUG,
def timeMethod(_func: Optional[Any] = None, *, metadata: Optional[MutableMapping] = None,
logger: Optional[logging.Logger] = None,
logLevel: int = logging.DEBUG) -> Callable:
"""Decorator to measure duration of a method.
"""Measure duration of a method.
Parameters
----------
Expand Down
7 changes: 7 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ lsst.utils = py.typed

[pydocstyle]
convention = numpy
# Our coding style does not require docstrings for magic methods (D105)
# Our docstyle documents __init__ at the class level (D107)
# We allow methods to inherit docstrings and this is not compatible with D102.
# Docstring at the very first line is not required
# D200, D205 and D400 all complain if the first sentence of the docstring does
# not fit on one line.
add-ignore = D107, D105, D102, D100, D200, D205, D400

# The ignore list for flake8 itself when run on the command line is distinct
# from the ignore list used by the pytest-flake8 plugin. This provides more
Expand Down
1 change: 1 addition & 0 deletions ups/utils.table
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ setupRequired(cpputils)
setupRequired(sconsUtils)

envPrepend(PYTHONPATH, ${PRODUCT_DIR}/python)
envPrepend(MYPYPATH, ${PRODUCT_DIR}/python)

0 comments on commit 7409401

Please sign in to comment.