Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ Fixes:
- Fix 2 `TypeError`s raised in `get_citations(markup_text=...)` when
`clean_steps` was omitted or lacked `"html"`. The documented fallback that
prepends the `html` step was both unreachable and broken.
- Keep a statute's trailing `note` designator as part of the citation's
identity. A note is a different provision than the section it is filed
under, so `42 U.S.C. § 1983 note` now carries `groups["note"]` and no
longer resolves to the bare `42 U.S.C. § 1983`. Prose such as
"§ 1983 notes that ..." is not treated as a designator. #323

## Current

Expand Down
4 changes: 4 additions & 0 deletions eyecite/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,10 @@ def add_law_metadata(citation: FullLawCitation, words: Tokens) -> None:
return

citation.full_span_end = citation.span()[1] + m.end()
# A note is a different provision than the section it is filed under, so it
# belongs to the citation's identity, which is built from groups.
if m["note"]:
citation.groups["note"] = m["note"]
citation.metadata.pin_cite = clean_pin_cite(m["pin_cite"]) or None
citation.metadata.publisher = m["publisher"]
citation.metadata.day = m["day"]
Expand Down
2 changes: 2 additions & 0 deletions eyecite/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ def corrected_citation_full(self):
m = self.metadata
if m.pin_cite:
parts.append(f"{m.pin_cite}")
if note := self.groups.get("note"):
parts.append(f" {note}")
publisher_date = " ".join(
i for i in (m.publisher, m.month, m.day, m.year) if i
)
Expand Down
17 changes: 17 additions & 0 deletions eyecite/regexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ def reference_pin_cite_re(regexes):
)
"""

# Law note regex:
# A trailing "note" points at material filed under a section rather than at the
# section itself, so it is a different target and not a pin cite. Unlike
# "et seq." the word is ordinary English, so it only counts as a designator
# when nothing continues it: "notes that ..." must not match.
LAW_NOTE_REGEX = r"""
(?:
\ (?P<note>note)
(?=
[,.;)\]\\]| # ending punctuation
\ ?[(\[]| # space and start of parens
$ # end of text
)
)?
"""

# Short cite antecedent regex:
# What case does a short cite refer to? For now, we just capture the previous
# word optionally followed by a comma. Example: Adarand, 515 U.S. at 241.
Expand Down Expand Up @@ -363,6 +379,7 @@ def reference_pin_cite_re(regexes):
# and then may be followed by a parenthetical:
POST_LAW_CITATION_REGEX = rf"""
{LAW_PIN_CITE_REGEX}?
{LAW_NOTE_REGEX}
\ ?
(?:\(
# Consol., McKinney, Deering, West, LexisNexis, etc.
Expand Down
37 changes: 37 additions & 0 deletions tests/test_FindTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,43 @@ def test_find_law_citations(self):
[law_citation('Mass. Gen. Laws ch. 1, §§ 2-3',
reporter='Mass. Gen. Laws',
groups={'chapter': '1', 'section': '2-3'})]),
# note designator, a different provision than the section
('42 U.S.C. § 1983 note',
[law_citation('42 U.S.C. § 1983 note', reporter='U.S.C.',
groups={'title': '42', 'section': '1983',
'note': 'note'})]),
('28 U.S.C. § 994 note',
[law_citation('28 U.S.C. § 994 note', reporter='U.S.C.',
groups={'title': '28', 'section': '994',
'note': 'note'})]),
('8 U.S.C. § 1101 note',
[law_citation('8 U.S.C. § 1101 note', reporter='U.S.C.',
groups={'title': '8', 'section': '1101',
'note': 'note'})]),
# note after a subsection pin cite
('Fla. Stat. § 120.68(2) note',
[law_citation('Fla. Stat. § 120.68(2) note',
reporter='Fla. Stat.',
metadata={'pin_cite': '(2)'},
groups={'section': '120.68', 'note': 'note'})]),
# note before a year parenthetical
('42 U.S.C. § 1983 note (1994)',
[law_citation('42 U.S.C. § 1983 note (1994)', reporter='U.S.C.',
groups={'title': '42', 'section': '1983',
'note': 'note'},
year=1994)]),
# et seq. is part of the section, so it stays a pin cite
('42 U.S.C. § 1983 et seq.',
[law_citation('42 U.S.C. § 1983 et seq.', reporter='U.S.C.',
metadata={'pin_cite': 'et seq.'},
groups={'title': '42', 'section': '1983'})]),
# "note" as prose, not a designator
('42 U.S.C. § 1983 notes that liability attaches',
[law_citation('42 U.S.C. § 1983', reporter='U.S.C.',
groups={'title': '42', 'section': '1983'})]),
('42 U.S.C. § 1983. Note that the court',
[law_citation('42 U.S.C. § 1983', reporter='U.S.C.',
groups={'title': '42', 'section': '1983'})]),
)
# fmt: on
self.run_test_pairs(test_pairs, "Law citation extraction")
Expand Down
20 changes: 20 additions & 0 deletions tests/test_ModelsTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ def test_unknown_citation_comparison(self):
self.assertNotEqual(hash(citations[0]), hash(citations[1]))
print("✓")

def test_law_note_comparison(self):
"""Is a statutory note a different citation than the section it is
filed under?"""
citations = [
get_citations("42 U.S.C. § 1983")[0],
get_citations("42 U.S.C. § 1983 note")[0],
]
print("Testing law note comparison...", end=" ")
self.assertNotEqual(citations[0], citations[1])
self.assertNotEqual(hash(citations[0]), hash(citations[1]))
print("✓")

def test_missing_page_cite_conversion(self):
"""Do citations with missing page numbers get their groups['page']
attribute set to None?"""
Expand Down Expand Up @@ -222,6 +234,14 @@ def test_corrected_full_citation_includes_closing_parenthesis(self):
"Meritor Sav. Bank v. Vinson, 477 U.S. 57, 60 (scotus 1986)",
)

def test_corrected_full_citation_includes_law_note(self):
"""Does the corrected_citation_full method keep a note designator?"""
law_note_citation = get_citations("42 U.S.C. § 1983 note")[0]
self.assertEqual(
law_note_citation.corrected_citation_full(),
"42 U.S.C. § 1983 note",
)

def test_page_correction(self):
"""Can we correct pages on citation.corrected_citation()?"""
tests = [
Expand Down
Loading