Skip to content

Commit

Permalink
Merge pull request #13 from welchbj/feature/populate-docstrings
Browse files Browse the repository at this point in the history
Merge feature/populate-docstrings
  • Loading branch information
welchbj committed Jan 2, 2022
2 parents ff89f35 + a9d5664 commit 91db592
Show file tree
Hide file tree
Showing 21 changed files with 115 additions and 68 deletions.
33 changes: 3 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,13 @@ Into this:
## Installation

You can download the latest packaged version from PyPI:

```sh
pip install almanac
```

Alternatively, you can get the bleeding-edge version from version control:

```sh
pip install https://github.com/welchbj/almanac/archive/master.tar.gz
```
Expand All @@ -122,33 +124,4 @@ pip install https://github.com/welchbj/almanac/archive/master.tar.gz

The original content of this repository is licensed under the [MIT License](https://opensource.org/licenses/MIT), as per the [LICENSE.txt](./LICENSE.txt) file.

Some of the parsing logic is borrowed from the [python-nubia](https://github.com/facebookincubator/python-nubia) project and is licensed under that project's [BSD License](https://github.com/facebookincubator/python-nubia/blob/master/LICENSE). For more information, please see the comment in [`almanac/parsing/parsing.py`](almanac/parsing/parsing.py).

## Development

Development dependencies can be installed with:

```sh
pip install -r deps/dev-requirements.txt
```

To run the tests, use:

```sh
python tasks.py test
```

To lint and type check the code, use:

```sh
flake8 .
mypy .
```

When it's time to cut a release, use:

```sh
python setup.py bdist_wheel sdist
twine check dist/*.whl dist/*.gz
twine upload dist/*.whl dist/*.gz
```
Some of the parsing logic is borrowed from the [python-nubia](https://github.com/facebookincubator/python-nubia) project and is licensed under that project's [BSD License](https://github.com/facebookincubator/python-nubia/blob/master/LICENSE). For more information, please see the comment in [`almanac/parsing/parsing.py`](almanac/parsing/parsing.py).
19 changes: 18 additions & 1 deletion almanac/arguments/argument_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@


class ArgumentBase(ABC):
"""A class for encapsulating a command argument."""
"""An abstract class for encapsulating a command argument.
This abstract base class is extended into two variants:
* :class:`~almanac.arguments.frozen_argument.FrozenArgument`
* :class:`~almanac.arguments.mutable_argument.MutableArgument`
It is unlikely that you should need to manually instantiate instances of these
classes, as they are mainly used internally for the argument-generating decorators
accessible via :class:`~almanac.core.application.Application`.
"""

def __init__(
self,
Expand Down Expand Up @@ -132,36 +143,42 @@ def annotation(
def is_pos_only(
self
) -> bool:
"""Whether this argument is a positional-only argument."""
return self._param.kind == self._param.POSITIONAL_ONLY

@property
def is_kw_only(
self
) -> bool:
"""Whether this argument is a keyword-only argument."""
return self._param.kind == self._param.KEYWORD_ONLY

@property
def is_var_kw(
self
) -> bool:
"""Whether this argument is a \*\*kwargs variant.""" # noqa
return self._param.kind == self._param.VAR_KEYWORD

@property
def is_var_pos(
self
) -> bool:
"""Whether this argument is an \*args variant.""" # noqa
return self._param.kind == self._param.VAR_POSITIONAL

@property
def has_default_value(
self
) -> bool:
"""Whether this argument has a default value."""
return self._param.default is not self._param.empty

@property
def default_value(
self
) -> Any:
"""The default value of this argument (if it has one)."""
return self._param.default

def __str__(
Expand Down
6 changes: 6 additions & 0 deletions almanac/arguments/frozen_argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@


class FrozenArgument(ArgumentBase):
"""An encapsulation of an argument which can no longer be mutated.
Setters for the various fields that can be mutated :class:`MutableCommand` will
raise :class:`~almanac.errors.argument_errors.FrozenAccessError`.
"""

def _abstract_display_name_setter(
self,
Expand Down
1 change: 1 addition & 0 deletions almanac/arguments/mutable_argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


class MutableArgument(ArgumentBase):
"""An encapsulation of an argument which can be mutated."""

def _abstract_display_name_setter(
self,
Expand Down
13 changes: 12 additions & 1 deletion almanac/commands/command_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@


class CommandBase(ABC):
"""Encapsulation of a command."""
"""The abstract base class for command types.
This ABC is extended into two variants:
* :class:`~almanac.commands.frozen_command.FrozenCommand`
* :class:`~almanac.commands.mutable_command.MutableCommand`
It is unlikely that you should need to manually instantiate instances of these
classes, as they are mainly used internally for the command-generating decorators
accessible via :class:`~almanac.core.application.Application`.
"""

def __init__(
self,
Expand Down
4 changes: 2 additions & 2 deletions almanac/commands/mutable_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class MutableCommand(CommandBase, MutableMapping[str, MutableArgument]):
The arguments of this command can be accessed via dict-like operations on instances
of this class. Of note is that the keys for this dictionary are the argument names
of the internal coroutine from this function (in contrast to a
:class:`FrozenCommand`, which is keyed based on a :class:`ArgumentBase`'s
``display_name`` property).
:class:`~almanac.commands.frozen_command.FrozenCommand`, which is keyed based on a
:class:`~almanac.arguments.argument_base.ArgumentBase`'s ``display_name`` property).
"""

Expand Down
1 change: 1 addition & 0 deletions almanac/constants/exit_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

@unique
class ExitCodes(IntEnum):
"""Exit codes for command executions."""
OK = 0

ERR_COMMAND_PARSING = auto()
Expand Down
8 changes: 7 additions & 1 deletion almanac/core/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@


class Application:
"""The core class of ``almanac``, wrapping everything together."""
"""The core class of ``almanac``, wrapping everything together.
This class should be your main entrypoint into all of the functionality provided
by this framework. The main method of customizing the state of an application is via
different decorators.
"""

def __init__(
self,
Expand Down
8 changes: 4 additions & 4 deletions almanac/core/command_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ def get(
self,
name_or_alias: str
) -> FrozenCommand:
"""Get a :class:`Command` by its name or alias.
"""Get a :class:`FrozenCommand` by its name or alias.
Returns:
The mapped :class:`Command` instance.
The mapped :class:`FrozenCommand` instance.
Raises:
NoSuchCommandError: If the specified ``name_or_alias`` is not contained
within this instance.
:class:`NoSuchCommandError`: If the specified ``name_or_alias`` is not
contained within this instance.
"""
if name_or_alias not in self._command_lookup_table.keys():
Expand Down
2 changes: 1 addition & 1 deletion almanac/io/abstract_io_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


class AbstractIoContext(ABC):
"""An interface for input/output contexts."""
"""The base abstract interface for input/output contexts."""

@abstractmethod
def info(
Expand Down
6 changes: 3 additions & 3 deletions almanac/pages/abstract_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class AbstractPage(ABC):
"""The base page interface."""
"""The base abstract page interface."""

def __init__(
self,
Expand Down Expand Up @@ -35,8 +35,8 @@ def info_text(
) -> str:
"""The info text about this page.
Think of this as a more dynamic output (in contrast to
:meth:`help_text`), which reflect the current state of this page.
Think of this as a more dynamic output (in contrast to :meth:`help_text`),
which reflect the current state of this page.
"""

Expand Down
6 changes: 2 additions & 4 deletions almanac/pages/directory_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ class DirectoryPage(AbstractPage):
def help_text(
self
) -> str:
return (
'TODO: DirectoryPage help_text')
return 'TODO: DirectoryPage help_text'

@property
def info_text(
self
) -> str:
return (
'TODO: DirectoryPage info_text')
return 'TODO: DirectoryPage info_text'

def get_prompt(
self
Expand Down
14 changes: 8 additions & 6 deletions almanac/pages/page_navigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ def change_directory(
path.
Raises:
NoSuchPageError: If the specified destination is invalid or does not exist.
OutOfBoundsPageError: If the specified destination attempts to go above the
root directory.
PathSyntaxError: If a syntactical error occured during the path parsing.
:class:`NoSuchPageError`: If the specified destination is invalid or does
not exist.
:class:`OutOfBoundsPageError`: If the specified destination attempts to go
above the root directory.
:class:`PathSyntaxError`: If a syntactical error occured during the path
parsing.
"""
try:
Expand Down Expand Up @@ -108,8 +110,8 @@ def explode(
this :class:`PageNavigator`.
Raises:
OutOfBoundsPageError: If invalid parent directors are referenced via the
`..` operator.
:class:`OutOfBoundsPageError`: If invalid parent directors are referenced
via the `..` operator.
"""
path = str(path)
Expand Down
2 changes: 1 addition & 1 deletion almanac/pages/page_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def assert_absolute_path(
"""Assert that the specified path is absolut.
Raises:
PathSyntaxError: If the path is not absolute.
:class:`PathSyntaxError`: If the path is not absolute.
"""
if not str(path).startswith('/'):
Expand Down
1 change: 1 addition & 0 deletions almanac/parsing/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def _raw_parse_cmd_line(


class IncompleteToken:
"""Encapsulation of a token that could only be partially parsed."""

def __init__(
self,
Expand Down
2 changes: 1 addition & 1 deletion almanac/style/highlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def highlight_for_mimetype(
) -> str:
"""Return ANSI-escaped highlighted text, as per the .
If :param`mimetype` cannot be resolved, then :param`fallback_mimetype` will be used.
If ``mimetype`` cannot be resolved, then ``fallback_mimetype`` will be used.
If that cannot be resolved (or is ``None``), then the pygments ``ClassNotFound``
exception will be raised.
Expand Down
1 change: 0 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ Welcome to...

project_meta/release_notes.rst
project_meta/development_guide.rst
project_meta/contributors.rst
project_meta/prior_art.rst

.. toctree::
Expand Down
5 changes: 0 additions & 5 deletions docs/project_meta/contributors.rst

This file was deleted.

36 changes: 31 additions & 5 deletions docs/project_meta/development_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ Development Guide
Setting up a development environment
------------------------------------

TODO
It is recommended to conduct development of this library in a `virtual environment <https://docs.python.org/3/library/venv.html>`_.

To install the development dependencies, use:

.. code-block:: shell
pip install -r deps/dev-requirements.txt
-------
Testing
Expand All @@ -15,7 +21,7 @@ Testing
Overview
~~~~~~~~

TODO
``almanac``'s testing strategy is split up into two categories: documentation tests and unit tests. Documentation tests mainly serve to verify that the code examples in the Python docstrings and in the documentation site do not become out of date. This is a nice complement to the unit tests, which enforce the public contract of this framework.

Doctests
~~~~~~~~
Expand All @@ -31,7 +37,7 @@ To test all of the doctests (across the library code and the documentation site
Unit tests
~~~~~~~~~~

To run all of the unit tests, use:
Unit tests are written to be executed via `pytest <https://docs.pytest.org/en/stable/>`_. To run all of the unit tests, use:

.. code-block:: shell
Expand All @@ -53,10 +59,30 @@ To run a single unit test, use something like:
Linting
-------

TODO
All of the code in this framework is style-linted with `flake8 <https://flake8.pycqa.org/en/latest/>`_ and type-linted with `mypy <https://mypy.readthedocs.io/en/stable/>`_. An `EditorConfig <https://editorconfig.org/>`_ configuration file is also included in the root of this project's repository to aid in style-conforming automatic formatting.

Running the linters is fairly straightforward:

.. code-block:: shell
flake8 .
mypy .
--------
Releases
--------

TODO
Release builds are performed using `setuptools <https://setuptools.readthedocs.io/en/latest/>`_, with the extra layer of convenience provided by `twine <https://twine.readthedocs.io/en/latest/>`_.

When it's time to cut a release, use the following steps:

.. code-block:: shell
# Build the source tarball and wheel.
python setup.py bdist_wheel sdist
# Verify that PyPI will accept our upload.
twine check dist/*.whl dist/*.gz
# Upload to PyPI.
twine upload dist/*.whl dist/*.gz

0 comments on commit 91db592

Please sign in to comment.