Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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