- add `lalib.fields.base.Field`, a blueprint for all concrete fields,
providing a unified interface to be used outside of the
`lalib.fields` sub-package
- implement `lalib.fields.complex_.ComplexField`, or `C` for short,
the field over the complex numbers (modeled as `complex` numbers)
- implement `lalib.fields.galois.GaloisField2`, or `GF2` for short,
the (finite) field over the two elements `one` and `zero`
+ adapt `lalib.elements.galois.GF2Element.__eq__()` to return
`NotImplemented` instead of `False` for non-castable `other`s
=> this fixes a minor issue with `pytest.approx()`
- implement `lalib.fields.rational.RationalField`, or `Q` for short,
the field over the rational numbers (modeled as `fractions.Fraction`s)
- implement `lalib.fields.real.RealField`, or `R` for short,
the field over the real numbers (modeled as `float`s)
- organize top-level imports for `lalib.fields`,
making `Q`, `R`, `C`, and `GF2` importable with
`from lalib.fields import *`
- provide extensive unit and integration tests for the new objects:
+ test generic and common behavior in `tests.fields.test_base`
+ test specific behavior is other modules
+ test the well-known math axioms for all fields (integration tests)
+ test the new objects' docstrings
+ add "pytest-repeat" to run randomized tests many times
- the `gf2` sub-classes `GF2One` and `GF2Zero` have no purpose
other than to provide unique `help(one)` and `help(zero)` messages
- delete them at the bottom of `lalib.elements.galois`
to prevent them from being imported at all
(`type(one)` and `type(zero)` still appear to be the `gf2` class,
which is a little white lie, provided by some meta class)
- differentiate between official and inofficial API in the test suite
- `to_gf2()` is only to be used within `GF2Element.__new__()`
=> rename it into `_to_gf2()`
- the test cases in `TestGF2ConstructorWithCastedValue`
cover everything in `TestGF2Casting`
=> retire the redundant test cases
and make the test suite run faster
- the future (concrete) Galois `Field` implementation
shall receive the name `GF2` (as per common math notation)
=> name conflict with the current `GF2` class
implementing the elements of the future Galois `Field`
=> rename the current `GF2` class into `GF2Element`
- because `GF2Element` is a bit tedius to type for the end user,
we introduce a `gf2` alias in line with the naming convention
for the built-in data types (e.g., `int` or `float`)
that are also used as elements of (other) `Field`s
=> name conflict with the current `lalib.elements.gf2` module
=> rename the module into `lalib.elements.galois`
- adjust the docstrings to refer to "the `gf2` type"
- adjust the top-level imports and tests
- make `GF2`, `one`, and `zero`, defined in the `lalib.elements.gf2`
module, available as top-level imports for the `lalib` package
via `from lalib import *`
- provide some code snippets in the package's docstring
- test the star import
- make `GF2`, `one`, and `zero`, defined in the `lalib.elements.gf2`
module, available as top-level imports in the `lalib.elements`
sub-package via `from lalib.elements import *`
- provide some code snippets in the sub-package's docstring
- test the star import
- add `GF2` class in the `lalib.elements` sub-package
implementing a typical Galois field with two elements
- the singleton objects `one` and `zero` are the concrete
instances of the `GF2` type for the end users
- besides the typical Galois arithmetic, `one` and `zero`
behave like the built-in numbers `1` and `0`
and implement the `numbers.Rational` interface
- add exhaustive docstrings with usage examples
- add (unit) test cases with 100% coverage
Whereas nox-poetry is a nice project,
it is not (yet) widely supported.
The `install_pinned()` function inside the "noxfile.py"
achieves most of its functionality.
Also, there is a chance poetry will be replaced in this project.
So, we no longer drag nox-poetry along.
The xdoctest integration in pytest (see tests/test_docstrings.py)
is prone to miss docstrings in new source files as they must be
included in the test case explicitly.
So, to play it safe, we run the nox session "test-docstrings" on CI.
- add pre-commit hooks:
+ run `nox -s lint` on staged *.py files
+ run common pre-commit hooks for validations that could not be
achieved with tools in the develop environment so easily
- add pre-merge hook:
+ run `nox -s _pre-commit-test-hook` before merges
* ignores the paths to staged files
passed in by the pre-commit framework
* runs all test cases instead
- use pip-audit to check the project's dependencies for known
security vulnerabilities and exploits
- add nox sessions "audit" and "audit-updates" to run
the above checks against the pinned and unpinned dependencies
- the nox session "test-coverage" triggers further nox sessions
that run the test suite for all supported Python versions
- the nox session "_test-coverage-run" runs the test suite for
a particular Python version using the coverage tool
- the nox session "_test-coverage-report" combines the individual
coverage reports
- use xdoctest to validate code snippets in docstrings
- make xdoctest part of the nox session "test" via
the new `test_docstrings()` test case
- add nox session "test-docstrings" for convenience;
also, `xdoctest.doctest_module()` does not discover
docstrings that are imported at the root of the package
=> each new module with docstrings must be added to
`test_docstrings()` by hand, which is likely forgotten
=> the nox session "test-docstrings" should run on CI
- `lalib.__version__` is dynamically assigned
- the format is "x.y.z[.dev0|aN|bN|rcN|.postM]"
where x, y, z, M, and N are non-negative integers
+ x, y, and z model the "major", "minor", and "patch"
parts of semantic versioning
(See: https://semver.org/#semantic-versioning-200)
+ M is a single digit and N either 1 or 2
=> This complies with (a strict subset of) PEP440
- add unit tests for the `__version__` identifier
- use pytest as the test suite and
measure test coverage with coverage.py
- add package for the test suite under tests/
- add nox session "test" to run the test suite
for all supported Python versions
- use flake8 to lint pytest for consistent style
- use flake8 as the main linting tool with the following plug-ins:
+ flake8-annotations
+ flake8-bandit
+ flake8-black
+ flake8-broken-line
+ flake8-bugbear
+ flake8-commas
+ flake8-comprehensions
+ flake8-debugger
+ flake8-docstrings
+ flake8-eradicate
+ flake8-isort
+ flake8-quotes
+ flake8-string-format
+ flake8-pyproject
+ pep8-naming
+ pydoclint
- use mypy for static type checking
- use ruff for linting for future compatibility
- add nox session "lint" to run these tools
- add `ruff check --fix ...` in nox session "format"
- lint all source files => no errors found
- describe how a local develop environment can be set up
- we use poetry to manage the project
=> add pyproject.toml and poetry.lock files
- add a package for the source code
=> "src" layout structure to ensure that pytest runs the tests
against a packaged version installed in a virtual environment
and not the *.py files in the project directory
(Source: https://hynek.me/articles/testing-packaging/)
- ignore poetry's artifacts in git