Skip to content

Commit

Permalink
Procedure uses EnactmentGroups
Browse files Browse the repository at this point in the history
  • Loading branch information
mscarey committed Mar 24, 2021
1 parent 3b24725 commit 5d65472
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 57 deletions.
77 changes: 34 additions & 43 deletions authorityspoke/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
from typing import Any, ClassVar, Dict, Iterable, Iterator, Type
from typing import List, Optional, Sequence, Tuple, Union

from dataclasses import dataclass

from legislice.enactments import Enactment, consolidate_enactments
from legislice.enactments import Enactment
from legislice.groups import EnactmentGroup

from nettlesome.terms import (
Comparable,
Expand All @@ -26,7 +25,6 @@
from authorityspoke.procedures import Procedure


@dataclass()
class Rule(Comparable):
r"""
A statement of a legal doctrine about a :class:`.Procedure` for litigation.
Expand Down Expand Up @@ -74,32 +72,35 @@ class Rule(Comparable):
object.
"""

procedure: Procedure
enactments: Sequence[Enactment] = ()
enactments_despite: Sequence[Enactment] = ()
mandatory: bool = False
universal: bool = False
generic: bool = False
absent: bool = False
name: Optional[str] = None

context_factor_names: ClassVar[Tuple[str, ...]] = ("procedure",)
enactment_attr_names: ClassVar[Tuple[str, ...]] = (
"enactments",
"enactments_despite",
)

def __post_init__(self):
def __init__(
self,
procedure: Procedure,
enactments: Optional[
Union[Enactment, EnactmentGroup, Sequence[Enactment]]
] = None,
enactments_despite: Optional[
Union[Enactment, EnactmentGroup, Sequence[Enactment]]
] = None,
mandatory: bool = False,
universal: bool = False,
generic: bool = False,
absent: bool = False,
name: Optional[str] = None,
):
self.procedure = procedure
self.enactments = EnactmentGroup(enactments)
self.enactments_despite = EnactmentGroup(enactments_despite)
self.mandatory = mandatory
self.universal = universal
self.generic = generic
self.absent = False

for attr in self.enactment_attr_names:
value = self.__dict__[attr]
if not value:
object.__setattr__(self, attr, ())
elif isinstance(value, Iterable):
object.__setattr__(self, attr, tuple(value))
else:
object.__setattr__(self, attr, (value,))
self.name = name

@property
def despite(self):
Expand Down Expand Up @@ -257,8 +258,7 @@ def add_enactment(self, incoming: Enactment) -> Rule:
if not isinstance(incoming, Enactment):
raise TypeError

new_enactments = list(self.enactments) + [incoming]
new_enactments = consolidate_enactments(new_enactments)
new_enactments = self.enactments + incoming
result = deepcopy(self)
result.set_enactments(new_enactments)
return result
Expand All @@ -276,8 +276,7 @@ def add_enactment_despite(self, incoming: Enactment) -> Rule:
if not isinstance(incoming, Enactment):
raise TypeError

new_enactments = list(self.enactments_despite) + [incoming]
new_enactments = consolidate_enactments(new_enactments)
new_enactments = self.enactments_despite + incoming
result = deepcopy(self)
result.set_enactments_despite(new_enactments)
return result
Expand Down Expand Up @@ -521,12 +520,8 @@ def _union_with_rule(self, other: Rule, context: ContextRegister) -> Optional[Ru
if new_procedure is None:
return None

enactments = consolidate_enactments(
list(self.enactments) + list(other.enactments)
)
enactments_despite = consolidate_enactments(
list(self.enactments_despite) + list(other.enactments_despite)
)
enactments = self.enactments + other.enactments
enactments_despite = self.enactments_despite + other.enactments_despite

if self.procedure.implies_all_to_all(
other.procedure, context=context
Expand Down Expand Up @@ -594,19 +589,15 @@ def set_despite(self, factors: Sequence[Factor]) -> None:
def set_outputs(self, factors: Sequence[Factor]) -> None:
self.procedure.set_outputs(factors)

def set_enactments(self, enactments: Union[Enactment, Sequence[Enactment]]) -> None:
if isinstance(enactments, Enactment):
self.enactments = (enactments,)
else:
self.enactments = tuple(enactments)
def set_enactments(
self, enactments: Union[Enactment, Sequence[Enactment], EnactmentGroup]
) -> None:
self.enactments = EnactmentGroup(enactments)

def set_enactments_despite(
self, enactments: Union[Enactment, Sequence[Enactment]]
self, enactments: Union[Enactment, Sequence[Enactment], EnactmentGroup]
) -> None:
if isinstance(enactments, Enactment):
self.enactments_despite = (enactments,)
else:
self.enactments_despite = tuple(enactments)
self.enactments_despite = EnactmentGroup(enactments)

def __str__(self):
mandatory = "MUST" if self.mandatory else "MAY"
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
anchorpoint>=0.4.4
apispec[validation]~=4.3.0
marshmallow>=3.10
legislice>=0.4.1
git+git://github.com/mscarey/nettlesome.git@85a5c0c225696085a6a7c75572b1b1679f0bfab6#egg=nettlesome
git+git://github.com/mscarey/legislice.git@12e342bfcf522188b7cdb9fd71608f9aa065ecec#egg=legislice
git+git://github.com/mscarey/nettlesome.git@70162a82b1bbb3fbade1b7ccc1c50c52d2701d31#egg=nettlesome
pint>=0.15
python-dotenv
python-slugify
Expand Down
9 changes: 7 additions & 2 deletions tests/io/test_enactment.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def test_enactment_import_from_dict(self, fake_usc_client):
holding_brad = load_holdings("holding_brad.json")
holdings = readers.read_holdings(holding_brad, client=fake_usc_client)
enactments = holdings[0].enactments
assert enactments[0].selected_text().endswith("shall not be violated…")
assert any(
law.selected_text().endswith("shall not be violated…") for law in enactments
)

def test_false_as_selection(self, fake_usc_client):
input_enactment = self.test_enactment.copy()
Expand All @@ -62,7 +64,10 @@ def test_enactment_import_from_holding(self):
holding_cardenas = load_holdings("holding_cardenas.json")
holdings = readers.read_holdings(holding_cardenas)
enactment_list = holdings[0].enactments
assert "all relevant evidence is admissible" in enactment_list[0].text
assert any(
"all relevant evidence is admissible" in enactment.text
for enactment in enactment_list
)

def test_enactment_does_not_fail_for_excess_selector(self, fake_beard_client):
"""
Expand Down
9 changes: 5 additions & 4 deletions tests/test_enactments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from dotenv import load_dotenv
from legislice.download import Client

from legislice.enactments import Enactment, consolidate_enactments
from legislice.enactments import Enactment
from legislice.groups import EnactmentGroup
from legislice.schemas import EnactmentSchema

from nettlesome.entities import Entity
Expand Down Expand Up @@ -205,7 +206,7 @@ def test_consolidate_enactments(self, make_response):
search = schema.load(search_clause)
warrants = schema.load(warrants_clause)

consolidated = consolidate_enactments([fourth, search, warrants])
consolidated = EnactmentGroup([fourth, search, warrants])
assert len(consolidated) == 1
assert consolidated[0].means(fourth)

Expand All @@ -229,7 +230,7 @@ def test_consolidate_adjacent_passages(self, make_response):
and_inventors,
right_to_writings,
]
combined = consolidate_enactments(to_combine)
combined = EnactmentGroup(to_combine)
assert len(combined) == 2
assert any(
law.selected_text().startswith("To promote the Progress")
Expand All @@ -246,7 +247,7 @@ def test_do_not_consolidate_from_different_sections(self, make_response):
due_process_5.select("life, liberty, or property, without due process of law")
due_process_14.select("life, liberty, or property, without due process of law")

combined = consolidate_enactments([due_process_5, due_process_14])
combined = EnactmentGroup([due_process_5, due_process_14])
assert len(combined) == 2

def test_cannot_add_fact_to_enactment(self, watt_factor, e_search_clause):
Expand Down
8 changes: 2 additions & 6 deletions tests/test_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from dotenv import load_dotenv
from legislice.download import Client
from legislice.groups import EnactmentGroup
from nettlesome.terms import ContextRegister, means
from nettlesome.entities import Entity
from nettlesome.terms import Explanation
Expand Down Expand Up @@ -120,7 +121,7 @@ def test_factor_properties_for_rule(self, make_opinion_with_holding):
assert len(cardenas.holdings[1].despite) == 1

def test_single_enactment_converted_to_tuple(self, make_holding):
assert isinstance(make_holding["h2"].enactments, tuple)
assert isinstance(make_holding["h2"].enactments, EnactmentGroup)

def test_holding_len(self, make_holding):
assert len(make_holding["h1"]) == 2
Expand Down Expand Up @@ -737,11 +738,6 @@ def test_add_universal_to_universal_irrelevant(self, make_procedure):
def test_rule_requiring_more_enactments_will_add(
self, e_due_process_5, make_complex_rule
):
"""
This requirement might be changed, so that if the second
Rule requires more Enactments the method will just assume they're
available.
"""
due_process_rule = (
make_complex_rule["accept_murder_fact_from_relevance"] + e_due_process_5
)
Expand Down

0 comments on commit 5d65472

Please sign in to comment.