Skip to content

Commit

Permalink
Updated documentation, tests, and implementation of check-invariants
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahsonder committed Jul 24, 2023
1 parent f2858c6 commit 5187329
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- For the contract checking `new_setattr` function, any variables that depend only on `klass` are now defined in the
outer function, efficiency of code was improved, and the attribute value is now restored to the original value if the
`_check_invariants` call raises an error.
- Can now check explicitly for whether the representation invariants of an object are satisfied.
- Added new function `check_invariants` which takes in an object and checks that the representation invariants of the object are satisfied.

### Bug Fixes

Expand Down
9 changes: 7 additions & 2 deletions docs/contracts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ AssertionError: divide argument '2' did not match type annotation for parameter

## API

The `python_ta.contracts` module offers two functions for enabling contract checking.
The `python_ta.contracts` module offers three functions for enabling contract checking.
The first, `check_all_contracts`, enables contract checking for all functions and classes defined within a module or set of modules.
The second, `check_contracts`, is a decorator allowing more fine-grained control over which
functions/classes have contract checking enabled.
functions/classes have contract checking enabled. The third, `check_invariants`, takes in an object and checks
that the representation invariants of the object are satisfied.

```{eval-rst}
.. autofunction:: python_ta.contracts.check_all_contracts
Expand All @@ -61,6 +62,10 @@ functions/classes have contract checking enabled.
.. autofunction:: python_ta.contracts.check_contracts(func_or_class)
```

```{eval-rst}
.. autofunction:: python_ta.contracts.check_invariants(object)
```

You can set the `ENABLE_CONTRACT_CHECKING` constant to `True` to enable all contract checking.

```{eval-rst}
Expand Down
6 changes: 4 additions & 2 deletions python_ta/contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,5 +615,7 @@ def check_invariants(obj: object) -> None:
klass = obj.__class__
klass_mod = _get_module(klass)

_set_invariants(klass)
_check_invariants(obj, klass, klass_mod.__dict__)
try:
_check_invariants(obj, klass, klass_mod.__dict__)
except PyTAContractError as e:
raise AssertionError(str(e)) from None
23 changes: 15 additions & 8 deletions tests/test_check_invariants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,47 @@
Test suite for checking the functionality of check_invariants.
"""

from typing import List

import pytest

from python_ta.contracts import PyTAContractError, check_invariants
from python_ta.contracts import check_contracts, check_invariants


@check_contracts
class Person:
"""A custom data type that represents data for a person.
Representation Invariants:
- self.age >= 0
- len(self.friends) > 1
"""

given_name: str
age: int
friends: List[str]

def __init__(self, given_name: str, age: int) -> None:
def __init__(self, given_name: str, age: int, friends: List[str]) -> None:
"""Initialize a new Person object."""
self.given_name = given_name
self.age = age
self.friends = friends


def test_no_errors() -> None:
"""Checks that check_invariants does not raise an error when representation invariants are satisfied."""
person_obj = Person("Jim", 50)
person = Person("Jim", 50, ["Pam", "Dwight"])

try:
check_invariants(person_obj)
check_invariants(person)
except Exception:
assert False
pytest.fail("check_invariants has incorrectly raised an error")


def test_raise_error() -> None:
"""Checks that check_invariants raises an error when representation invariants are violated."""
person_obj = Person("Jim", -50)
person = Person("Jim", 50, ["Pam", "Dwight"])
person.friends.pop()

with pytest.raises(PyTAContractError):
check_invariants(person_obj)
with pytest.raises(AssertionError):
check_invariants(person)

0 comments on commit 5187329

Please sign in to comment.