def explanations_implication( self, other: Comparable, context: Optional[Union[ContextRegister, Explanation]] = None, ) -> Iterator[ContextRegister]: """Yield contexts that would cause self and other to have same meaning.""" if not self.comparable_with(other): raise TypeError(f"Type Holding cannot be compared with type {type(other)}.") if not isinstance(context, Explanation): context = Explanation.from_context(context) if isinstance(other, Procedure): other = Rule(procedure=other) if isinstance(other, Rule): other = Holding(rule=other) if not isinstance(other, self.__class__): if context: context = context.reversed_context() if other.implied_by(self, context=context): yield context elif self.exclusive is other.exclusive is False: yield from self._explanations_implies_if_not_exclusive( other, context=context ) else: yield from self.nonexclusive_holdings.explanations_implication( other.nonexclusive_holdings, context=context )
def test_holding_flagged_exclusive(self, make_regime, make_enactment): """ Test that "exclusive" flag doesn't mess up the holding where it's placed. Test whether the Feist opinion object includes a holding with the output "Rural's telephone directory was copyrightable" and the input "Rural's telephone directory was original", when that holding was marked "exclusive" in the JSON. """ to_read = load_holdings("holding_feist.json") feist_holdings = readers.read_holdings(to_read, regime=make_regime) directory = Entity("Rural's telephone directory") original = Fact(Predicate("{} was an original work"), directory) copyrightable = Fact(Predicate("{} was copyrightable"), directory) originality_enactments = [ make_enactment["securing_for_authors"], make_enactment["right_to_writings"], make_enactment["copyright_requires_originality"], ] originality_rule = Rule( Procedure(outputs=copyrightable, inputs=original), mandatory=False, universal=False, enactments=originality_enactments, ) assert any( feist_holding.rule.means(originality_rule) for feist_holding in feist_holdings )
def test_add_simple_rules(self): """ A simple form of two Rules from Feist, with no Enactments. Even though the rules have different generic Factors (i.e. Entities), the __add__ function will make one Rule using the generic Factors from the operand on the left, but will give it the output from the operand on the right. """ context = Entity(name="the Pythagorean theorem") three = Entity(name="the number three") fact_not_original = Rule( procedure=Procedure( inputs=Fact(predicate=Predicate(content="$work was a fact"), terms=context), outputs=Fact( predicate=Predicate(content="$work was an original work", truth=False), terms=context, ), ), universal=True, ) unoriginal_not_copyrightable = Rule( procedure=Procedure( inputs=Fact( predicate=Predicate(content="$work was an original work", truth=False), terms=three, ), outputs=Fact( predicate=Predicate(content="${work} was copyrightable", truth=False), terms=three, ), ), universal=True, ) facts_not_copyrightable = fact_not_original + unoriginal_not_copyrightable assert len(facts_not_copyrightable.inputs) == 1 assert str(facts_not_copyrightable.inputs[0]).endswith( "act that <the Pythagorean theorem> was a fact") assert len(facts_not_copyrightable.outputs) == 2 assert "false that <the Pythagorean theorem> was copyrightable" in str( facts_not_copyrightable)
def test_add_universal_to_universal(self, make_factor, make_exhibit, make_complex_fact): accept_relevance_testimony_ALL = Rule( procedure=Procedure( inputs=make_exhibit["relevant_murder_testimony"], outputs=make_complex_fact["f_relevant_murder"], ), universal=True, ) accept_murder_fact_ALL = Rule( procedure=Procedure( inputs=make_complex_fact["f_relevant_murder"], outputs=make_factor["f_murder"], ), universal=True, ) result = accept_relevance_testimony_ALL + accept_murder_fact_ALL assert result.universal is True
def test_holding_flagged_exclusive( self, e_securing_exclusive_right_to_writings, e_copyright_requires_originality, make_response, ): """ Test that "exclusive" flag doesn't mess up the holding where it's placed. Test whether the Feist opinion object includes a holding with the output "Rural's telephone directory was copyrightable" and the input "Rural's telephone directory was original", when that holding was marked "exclusive" in the JSON. `originality_rule` will be a little broader because it's based on less Enactment text """ fake_client = FakeClient(responses=make_response) holdings = read_holdings_from_file("holding_feist.yaml", client=fake_client) directory = Entity(name="Rural's telephone directory") original = Fact( predicate=Predicate(content="$work was an original work"), terms=directory) copyrightable = Fact( predicate=Predicate(content="$work was copyrightable"), terms=directory) originality_enactments = [ e_securing_exclusive_right_to_writings, e_copyright_requires_originality, ] originality_rule = Rule( procedure=Procedure(outputs=copyrightable, inputs=original), mandatory=False, universal=False, enactments=originality_enactments, ) assert any( originality_rule.implies(feist_holding.rule) for feist_holding in holdings)
def test_is_beard_implied( self, facial_hair_over_5mm, facial_hair_on_or_below_chin, facial_hair_uninterrupted, outcome, fake_beard_client, make_beard_rule, ): beard = Entity(name="a facial feature") sec_4 = fake_beard_client.read("/test/acts/47/4/") was_facial_hair = Predicate(content="$thing was facial hair") fact_was_facial_hair = Fact(predicate=was_facial_hair, terms=beard) hypothetical = Rule( procedure=Procedure( inputs=[ fact_was_facial_hair, Fact( predicate=Comparison( content="the length of $thing was", sign=">=", expression=Q_("5 millimeters"), truth=facial_hair_over_5mm, ), terms=beard, ), Fact( predicate=Predicate( content="$thing occurred on or below the chin", truth=facial_hair_on_or_below_chin, ), terms=beard, ), Fact( predicate=Predicate( content= "$thing existed in an uninterrupted line from the front " "of one ear to the front of the other ear below the nose", truth=facial_hair_uninterrupted, ), terms=beard, ), ], outputs=Fact(predicate=Predicate(content="$thing was a beard"), terms=beard), ), enactments=sec_4, ) meets_chin_test = make_beard_rule[0].implies(hypothetical) meets_ear_test = make_beard_rule[1].implies(hypothetical) assert outcome == meets_chin_test or meets_ear_test
def test_is_beard_implied( self, facial_hair_over_5mm, facial_hair_on_or_below_chin, facial_hair_uninterrupted, outcome, make_beard_rule, ): beard = Entity("a facial feature") sec_4 = client.read(path="/test/acts/47/4/") hypothetical = Rule( procedure=Procedure( inputs=[ Fact(Predicate("{} was facial hair"), context_factors=beard), Fact( Predicate( "the length of {} was {}", comparison=">=", quantity=Q_("5 millimeters"), truth=facial_hair_over_5mm, ), context_factors=beard, ), Fact( Predicate( "{} occurred on or below the chin", truth=facial_hair_on_or_below_chin, ), context_factors=beard, ), Fact( Predicate( "{} existed in an uninterrupted line from the front " "of one ear to the front of the other ear below the nose", truth=facial_hair_uninterrupted, ), context_factors=beard, ), ], outputs=Fact(Predicate("{} was a beard"), context_factors=beard), ), enactments=sec_4, ) meets_chin_test = make_beard_rule[0].implies(hypothetical) meets_ear_test = make_beard_rule[1].implies(hypothetical) assert outcome == meets_chin_test or meets_ear_test
def test_implication_interchangeable_terms(self): ate_together = Predicate( content="$person1 ate at $place with $person2") shot = Predicate(content="$attacker shot $victim") murder = Predicate(content="$attacker murdered $victim") alice = Entity(name="Alice") bob = Entity(name="Bob") diane = Entity(name="Diane") ed = Entity(name="Ed") grove = Entity(name="Shady Grove") magnolia = Entity(name="Magnolia Cafe") alice_and_bob_rule = Rule( procedure=Procedure( outputs=(Fact(predicate=murder, terms=(alice, bob))), inputs=( Fact(predicate=ate_together, terms=(alice, grove, bob)), Fact(predicate=shot, terms=(alice, bob)), ), ), mandatory=True, universal=True, ) diane_and_ed_rule = Rule( procedure=Procedure( outputs=(Fact(predicate=murder, terms=(diane, ed))), inputs=( Fact(predicate=ate_together, terms=(ed, magnolia, diane)), Fact(predicate=shot, terms=(diane, ed)), ), ), mandatory=True, universal=True, ) assert alice_and_bob_rule.implies(diane_and_ed_rule)
def explanations_contradiction( self, other: Factor, context: ContextRegister = None ) -> Iterator[Explanation]: r""" Find context matches that would result in a contradiction with other. Works by testing whether ``self`` would imply ``other`` if ``other`` had an opposite value for ``rule_valid``. This method takes three main paths depending on whether the holdings ``self`` and ``other`` assert that rules are decided or undecided. A ``decided`` :class:`Rule` can never contradict a previous statement that any :class:`Rule` was undecided. If rule A implies rule B, then a holding that B is undecided contradicts a prior :class:`Rule` deciding that rule A is valid or invalid. :param other: The :class:`.Factor` to be compared to self. Unlike with :meth:`~Holding.contradicts`\, this method cannot be called with an :class:`.Opinion` for `other`. :returns: a generator yielding :class:`.ContextRegister`\s that cause a contradiction. """ if context is None: context = ContextRegister() if isinstance(other, Procedure): other = Rule(procedure=other) if isinstance(other, Rule): other = Holding(rule=other) if isinstance(other, self.__class__): yield from self._explanations_contradiction_of_holding(other, context) elif isinstance(other, Factor): yield from [] # no possible contradiction elif hasattr(other, "explanations_contradiction"): if context: context = context.reversed() yield from other.explanations_contradiction(self, context=context) else: raise TypeError( f"'Contradicts' test not implemented for types " f"{self.__class__} and {other.__class__}." )
def test_getting_factors_from_new_holding(self, make_opinion_with_holding): watt = make_opinion_with_holding["watt_majority"] watt.clear_holdings() elephants = Fact(predicate="$animal was an elephant", terms=Entity(name="the elephant")) mouseholes = Fact( predicate=Predicate(content="$animal hides in mouseholes", truth=False), terms=Entity(name="the elephant"), ) procedure = Procedure(inputs=elephants, outputs=mouseholes) rule = Rule(procedure=procedure) holding = Holding(rule=rule) watt.posit(holding) factors = watt.factors_by_name() factor = factors["the fact that <the elephant> was an elephant"] assert factor.terms[0].name == "the elephant"
def test_holding_inferred_from_exclusive(self, make_enactment, make_response): """ Test whether the Feist opinion object includes a holding that was inferred from an entry in the JSON saying that the "exclusive" way to reach the output "Rural's telephone directory was copyrightable" is to have the input "Rural's telephone directory was original". The inferred holding says that in the absence of the input "Rural's telephone directory was original", the court MUST ALWAYS find the output to be absent as well. Marked xfail because the "exclusive" flag no longer causes inferred Holdings to be expanded. Instead, it now should generate inferred Rules that aren't expanded during data loading. """ mock_client = FakeClient(responses=make_response) to_read = load_holdings("holding_feist.yaml") feist_holdings = readers.read_holdings(to_read["holdings"], client=mock_client) directory = Entity(name="Rural's telephone directory") not_original = Fact(Predicate(content="{} was an original work"), directory, absent=True) not_copyrightable = Fact(Predicate(content="{} was copyrightable"), directory, absent=True) no_originality_procedure = Procedure(outputs=not_copyrightable, inputs=not_original) no_originality_rule = Rule( no_originality_procedure, mandatory=True, universal=True, enactments=[ make_enactment["securing_for_authors"], make_enactment["right_to_writings"], make_enactment["copyright_requires_originality"], ], ) assert feist_holdings[4].rule.means(no_originality_rule)
def from_factors( self, outputs: FactorGroup, inputs: Optional[FactorGroup] = None, despite: Optional[FactorGroup] = None, enactments: Sequence[Enactment] = (), enactments_despite: Sequence[Enactment] = (), mandatory: bool = False, universal: bool = False, generic: bool = False, decided: bool = True, exclusive: bool = False, absent: bool = False, ): """Create new Holding without an existing Rule or Procedure.""" procedure = Procedure(inputs=inputs, outputs=outputs, despite=despite) rule = Rule( procedure=procedure, enactments=enactments, enactments_despite=enactments_despite, mandatory=mandatory, universal=universal, ) return Holding(rule=rule, generic=generic, decided=decided, exclusive=exclusive)
def test_dump_and_read_rule(self, make_procedure, e_search_clause): rule = Rule(procedure=make_procedure["c2"], enactments=e_search_clause) dumped = rule.dict() loaded = Rule(**dumped) content = loaded.despite[0].predicate.content assert "the distance between $place1 and $place2 was" in content