Skip to content

Commit

Permalink
test(entities): fix doctests (#1255)
Browse files Browse the repository at this point in the history
  • Loading branch information
bonjourmauko authored Oct 14, 2024
2 parents 66d0052 + 233df9e commit 7e219c3
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 81 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 43.1.0 [#1255](https://github.com/openfisca/openfisca-core/pull/1255)

#### New features

- Make `CoreEntity` public
- Allows for more easily creating customised entities.

#### Technical changes

- Add missing doctests.

# 43.0.0 [#1224](https://github.com/openfisca/openfisca-core/pull/1224)

#### Technical changes
Expand Down
29 changes: 6 additions & 23 deletions openfisca_core/entities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,23 @@
# Transitional imports to ensure non-breaking changes.
# Could be deprecated in the next major release.
#
# How imports are being used today:
#
# >>> from openfisca_core.module import symbol
#
# The previous example provokes cyclic dependency problems
# that prevent us from modularizing the different components
# of the library so to make them easier to test and to maintain.
#
# How could them be used after the next major release:
#
# >>> from openfisca_core import module
# >>> module.symbol()
#
# And for classes:
#
# >>> from openfisca_core import module
# >>> module.Symbol()
#
# See: https://www.python.org/dev/peps/pep-0008/#imports
"""Provide a way of representing the entities of a rule system."""

from . import types
from ._core_entity import CoreEntity
from .entity import Entity
from .group_entity import GroupEntity
from .helpers import build_entity, find_role
from .role import Role

SingleEntity = Entity
check_role_validity = CoreEntity.check_role_validity

__all__ = [
"CoreEntity",
"Entity",
"SingleEntity",
"GroupEntity",
"Role",
"SingleEntity",
"build_entity",
"check_role_validity",
"find_role",
"types",
]
126 changes: 101 additions & 25 deletions openfisca_core/entities/_core_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,27 @@
from .role import Role


class _CoreEntity:
class CoreEntity:
"""Base class to build entities from.
Args:
__key: A key to identify the ``_CoreEntity``.
__plural: The ``key`` pluralised.
__label: A summary description.
__doc: A full description.
*__args: Additional arguments.
*__args: Any arguments.
**__kwargs: Any keyword arguments.
Examples:
>>> from openfisca_core import entities
>>> from openfisca_core.entities import types as t
>>> class Entity(entities.CoreEntity):
... def __init__(self, key):
... self.key = t.EntityKey(key)
>>> Entity("individual")
Entity(individual)
"""

#: A key to identify the ``_CoreEntity``.
#: A key to identify the ``CoreEntity``.
key: t.EntityKey

#: The ``key`` pluralised.
Expand All @@ -33,27 +41,20 @@ class _CoreEntity:
#: A full description.
doc: str

#: Whether the ``_CoreEntity`` is a person or not.
#: Whether the ``CoreEntity`` is a person or not.
is_person: ClassVar[bool]

#: A ``TaxBenefitSystem`` instance.
_tax_benefit_system: None | t.TaxBenefitSystem = None

@abc.abstractmethod
def __init__(
self,
__key: str,
__plural: str,
__label: str,
__doc: str,
*__args: object,
) -> None: ...
def __init__(self, *__args: object, **__kwargs: object) -> None: ...

def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.key})"

def set_tax_benefit_system(self, tax_benefit_system: t.TaxBenefitSystem) -> None:
"""A ``_CoreEntity`` belongs to a ``TaxBenefitSystem``."""
"""A ``CoreEntity`` belongs to a ``TaxBenefitSystem``."""
self._tax_benefit_system = tax_benefit_system

def get_variable(
Expand All @@ -72,11 +73,46 @@ def get_variable(
None: When the ``Variable`` doesn't exist.
Raises:
ValueError: When the :attr:`_tax_benefit_system` is not set yet.
ValueError: When ``check_existence`` is ``True`` and
the ``Variable`` doesn't exist.
"""
Examples:
>>> from openfisca_core import (
... entities,
... periods,
... taxbenefitsystems,
... variables,
... )
>>> this = entities.SingleEntity("this", "", "", "")
>>> that = entities.SingleEntity("that", "", "", "")
>>> this.get_variable("tax")
Traceback (most recent call last):
ValueError: You must set 'tax_benefit_system' before calling thi...
>>> tax_benefit_system = taxbenefitsystems.TaxBenefitSystem([this])
>>> this.set_tax_benefit_system(tax_benefit_system)
>>> this.get_variable("tax")
>>> this.get_variable("tax", check_existence=True)
Traceback (most recent call last):
VariableNotFoundError: You tried to calculate or to set a value...
>>> class tax(variables.Variable):
... definition_period = periods.MONTH
... value_type = float
... entity = that
>>> this._tax_benefit_system.add_variable(tax)
<openfisca_core.entities._core_entity.tax object at ...>
>>> this.get_variable("tax")
<openfisca_core.entities._core_entity.tax object at ...>
"""
if self._tax_benefit_system is None:
msg = "You must set 'tax_benefit_system' before calling this method."
raise ValueError(
Expand All @@ -90,16 +126,47 @@ def check_variable_defined_for_entity(self, variable_name: t.VariableName) -> No
Args:
variable_name: The ``Variable`` to be found.
Returns:
Variable: When the ``Variable`` exists.
None: When the :attr:`_tax_benefit_system` is not set.
Raises:
ValueError: When the ``Variable`` exists but is defined
for another ``Entity``.
"""
Examples:
>>> from openfisca_core import (
... entities,
... periods,
... taxbenefitsystems,
... variables,
... )
>>> this = entities.SingleEntity("this", "", "", "")
>>> that = entities.SingleEntity("that", "", "", "")
>>> tax_benefit_system = taxbenefitsystems.TaxBenefitSystem([that])
>>> this.set_tax_benefit_system(tax_benefit_system)
>>> this.check_variable_defined_for_entity("tax")
Traceback (most recent call last):
VariableNotFoundError: You tried to calculate or to set a value...
>>> class tax(variables.Variable):
... definition_period = periods.WEEK
... value_type = int
... entity = that
>>> this._tax_benefit_system.add_variable(tax)
<openfisca_core.entities._core_entity.tax object at ...>
>>> this.check_variable_defined_for_entity("tax")
Traceback (most recent call last):
ValueError: You tried to compute the variable 'tax' for the enti...
>>> tax.entity = this
>>> this._tax_benefit_system.update_variable(tax)
<openfisca_core.entities._core_entity.tax object at ...>
>>> this.check_variable_defined_for_entity("tax")
"""
entity: None | t.CoreEntity = None
variable: None | t.Variable = self.get_variable(
variable_name,
Expand Down Expand Up @@ -132,11 +199,20 @@ def check_role_validity(role: object) -> None:
Raises:
ValueError: When ``role`` is not a ``Role``.
"""
Examples:
>>> from openfisca_core import entities
>>> role = entities.Role({"key": "key"}, object())
>>> entities.check_role_validity(role)
>>> entities.check_role_validity("hey!")
Traceback (most recent call last):
ValueError: hey! is not a valid role
"""
if role is not None and not isinstance(role, Role):
msg = f"{role} is not a valid role"
raise ValueError(msg)


__all__ = ["_CoreEntity"]
__all__ = ["CoreEntity"]
2 changes: 1 addition & 1 deletion openfisca_core/entities/_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

@dataclasses.dataclass(frozen=True)
class _Description:
"""A ``Role``'s description.
r"""A ``Role``'s description.
Examples:
>>> data = {
Expand Down
25 changes: 22 additions & 3 deletions openfisca_core/entities/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,37 @@
import textwrap

from . import types as t
from ._core_entity import _CoreEntity
from ._core_entity import CoreEntity


class Entity(_CoreEntity):
"""An entity (e.g. a person, a household) on which calculations can be run.
class Entity(CoreEntity):
r"""An entity (e.g. a person, a household) on which calculations can be run.
Args:
key: A key to identify the ``Entity``.
plural: The ``key`` pluralised.
label: A summary description.
doc: A full description.
Examples:
>>> from openfisca_core import entities
>>> entity = entities.SingleEntity(
... "individual",
... "individuals",
... "An individual",
... "\t\t\tThe minimal legal entity on which a rule might be a...",
... )
>>> repr(entities.SingleEntity)
"<class 'openfisca_core.entities.entity.Entity'>"
>>> repr(entity)
'Entity(individual)'
>>> str(entity)
'Entity(individual)'
"""

#: A key to identify the ``Entity``.
Expand Down
49 changes: 46 additions & 3 deletions openfisca_core/entities/group_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
from itertools import chain

from . import types as t
from ._core_entity import _CoreEntity
from ._core_entity import CoreEntity
from .role import Role


class GroupEntity(_CoreEntity):
"""Represents an entity containing several others with different roles.
class GroupEntity(CoreEntity):
r"""Represents an entity containing several others with different roles.
A ``GroupEntity`` represents an ``Entity`` containing several other entities,
with different roles, and on which calculations can be run.
Expand All @@ -26,6 +26,49 @@ class GroupEntity(_CoreEntity):
containing_entities: The list of keys of group entities whose members
are guaranteed to be a superset of this group's entities.
Examples:
>>> from openfisca_core import entities
>>> family_roles = [
... {
... "key": "parent",
... "subroles": ["first_parent", "second_parent"],
... }
... ]
>>> family = entities.GroupEntity(
... "family",
... "families",
... "A family",
... "\t\t\tAll the people somehow related living together.",
... family_roles,
... )
>>> household_roles = [
... {
... "key": "partners",
... "subroles": ["first_partner", "second_partner"],
... }
... ]
>>> household = entities.GroupEntity(
... "household",
... "households",
... "A household",
... "\t\t\tAll the people who live together in the same place.",
... household_roles,
... (family.key,),
... )
>>> repr(entities.GroupEntity)
"<class 'openfisca_core.entities.group_entity.GroupEntity'>"
>>> repr(household)
'GroupEntity(household)'
>>> str(household)
'GroupEntity(household)'
"""

#: A key to identify the ``Entity``.
Expand Down
Loading

0 comments on commit 7e219c3

Please sign in to comment.