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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- 1.11.7 [TBR]
- Cont. refactoring
- Separating XML parsing code from logical types

- 1.11.6 [26 October 2025]
- Fixed Voseo conjugation for irregular verb `ser` for the subjuntivo (no vowel accents)
Expand Down
5 changes: 3 additions & 2 deletions tests/test_inflectors/test_inflector_ca.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang
from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.person import Person
from verbecc.src.defs.types.data.tense_template import TenseTemplate
from verbecc.src.conjugator.conjugator import Conjugator
from verbecc.src.parsers.tense_template import TenseTemplate
from verbecc.src.parsers.tense_template_parser import TenseTemplateParser
from verbecc.src.defs.types.alternates_behavior import AlternatesBehavior


Expand Down Expand Up @@ -2985,7 +2986,7 @@ def test_inflector_ca_conjugate_simple_mood_tense(cg):
</present>""",
parser=None,
)
tense_template = TenseTemplate(Lang.ca, mood, tense_elem)
tense_template = TenseTemplateParser(Lang.ca, mood).parse(tense_elem)
out = cg._conjugate_simple_mood_tense(verb_stem, mood, tense, tense_template)
assert len(out) == 6
assert out == [
Expand Down
6 changes: 3 additions & 3 deletions tests/test_inflectors/test_inflector_es.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
)
from verbecc.src.defs.types.lang.es.voseo_options import VoseoOptions
from verbecc.src.conjugator.conjugator import Conjugator
from verbecc.src.parsers.tense_template import TenseTemplate
from verbecc.src.parsers.tense_template_parser import TenseTemplateParser

cg = Conjugator(lang="es")
cg = Conjugator(lang=Lang.es)


def test_all_verbs_have_templates():
Expand Down Expand Up @@ -635,7 +635,7 @@ def test_inflector_es_conjugate_simple_mood_tense():
</presente>""",
parser=None,
)
tense_template = TenseTemplate(Lang.es, mood, tense_elem)
tense_template = TenseTemplateParser(Lang.es, mood).parse(tense_elem)
out = cg._conjugate_simple_mood_tense(verb_stem, mood, tense, tense_template)
assert len(out) == 6
assert out == [
Expand Down
6 changes: 3 additions & 3 deletions tests/test_inflectors/test_inflector_fr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.person import Person
from verbecc.src.conjugator.conjugator import Conjugator, AlternatesBehavior
from verbecc.src.parsers.tense_template import TenseTemplate
from verbecc.src.parsers.tense_template_parser import TenseTemplateParser
from verbecc.src.defs.types.exceptions import ConjugatorError
from verbecc.src.defs.types.conjugation import MoodsConjugation

Expand Down Expand Up @@ -68,7 +68,7 @@ def test_inflector_frverb_can_be_reflexive(cg, infinitive, expected_result):
def test_inflector_fr_impersonal_verbs(cg):
impersonal_verbs = [
v.infinitive
for v in cg._inflector._verb_parser.verbs
for v in cg._inflector._verbs
if cg._inflector._is_impersonal_verb(v.infinitive)
]
assert set(impersonal_verbs) == set(
Expand Down Expand Up @@ -111,7 +111,7 @@ def test_inflector_fr_conjugate_simple_mood_tense(cg):
</présent>""",
parser=None,
)
tense_template = TenseTemplate(Lang.fr, mood, tense_elem)
tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem)
out = cg._conjugate_simple_mood_tense(verb_stem, mood, tense, tense_template)
assert len(out) == 6
assert out == [
Expand Down
2 changes: 1 addition & 1 deletion tests/test_inflectors/test_inflector_ro.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pytest

from verbecc.src.conjugator.conjugator import Conjugator
from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.person import Person
from verbecc.src.conjugator.conjugator import Conjugator
from verbecc.src.defs.types.alternates_behavior import AlternatesBehavior


Expand Down
2 changes: 1 addition & 1 deletion tests/test_mlconjug.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@pytest.fixture(scope="module")
def verb_template_pairs():
inf = InflectorFr()
yield [(v.infinitive, v.template) for v in inf._verb_parser.verbs]
yield [(v.infinitive, v.template) for v in inf._verbs]


def test_extract_verb_features():
Expand Down
6 changes: 3 additions & 3 deletions tests/test_parser/test_conjugation_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from verbecc.src.parsers.conjugation_template import ConjugationTemplate
from verbecc.src.parsers.conjugation_template_parser import ConjugationTemplateParser
from verbecc.src.defs.types.exceptions import ConjugationTemplateError
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang

Expand All @@ -11,11 +11,11 @@
def test_template_invalid_tag_template(mock_template_elem):
mock_template_elem.tag.return_value = "not-template"
with pytest.raises(ConjugationTemplateError):
template = ConjugationTemplate(Lang.fr, mock_template_elem)
template = ConjugationTemplateParser(Lang.fr).parse(mock_template_elem)


@patch("lxml.etree._Element")
def test_template_invalid_tag_name(mock_template_elem):
mock_template_elem.get.return_value = "not-name"
with pytest.raises(ConjugationTemplateError):
template = ConjugationTemplate(Lang.ca, mock_template_elem)
template = ConjugationTemplateParser(Lang.ca).parse(mock_template_elem)
7 changes: 4 additions & 3 deletions tests/test_parser/test_tense_and_person.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from lxml import etree

from verbecc.src.parsers.tense_template import TenseTemplate
from verbecc.src.parsers.tense_template_parser import TenseTemplateParser
from verbecc.src.defs.types.data.tense_template import TenseTemplate
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang
from verbecc.src.defs.types.tense import Tense

Expand All @@ -17,9 +18,9 @@ def test_tense_and_person():
</présent>"""
tense_elem: etree._Element = etree.fromstring(tense_elem_str)
tense = Tense.fr.Présent
tense_template = TenseTemplate(Lang.fr, mood, tense_elem)
tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem)
assert tense_template.mood == mood
assert tense_template.name == str(tense.value)
assert tense_template.tense == tense
assert tense_template.person_endings[0].get_ending() == "ie"
assert tense_template.person_endings[0].get_alternate_ending_if_available() == "ye"
assert tense_template.person_endings[0].get_person() == "1s"
Expand Down
15 changes: 15 additions & 0 deletions tests/test_parser/test_verb_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from unittest.mock import patch

import pytest

from verbecc.src.parsers.verb_parser import VerbParser
from verbecc.src.defs.types.exceptions import VerbsParserError
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang


@patch("lxml.etree._Element")
def test_verb_invalid_xml(mock_v_elem):
v = VerbParser()
mock_v_elem.tag.return_value = "not-v"
with pytest.raises(VerbsParserError):
v.parse(mock_v_elem)
48 changes: 4 additions & 44 deletions tests/test_parser/test_verbs_parser.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,8 @@
from unittest.mock import patch

import pytest

from verbecc.src.parsers.verbs_parser import VerbsParser
from verbecc.src.parsers.verb import Verb
from verbecc.src.defs.types.exceptions import VerbsParserError
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang


def test_verbs_parser():
vp = VerbsParser()
assert len(vp.verbs) >= 7000


def test_verb():
vp = VerbsParser()
verb = vp.find_verb_by_infinitive("manger")
assert verb.infinitive == "manger"
assert verb.template == "man:ger"
assert verb.translation_en == "eat"


def test_verb_two():
vp = VerbsParser()
verb = vp.find_verb_by_infinitive("abattre")
assert verb.infinitive == "abattre"
assert verb.template == "bat:tre"
assert verb.translation_en == "tear down"


@patch("lxml.etree._Element")
def test_verb_invalid_xml(mock_v_elem):
mock_v_elem.tag.return_value = "not-v"
with pytest.raises(VerbsParserError):
v = Verb(mock_v_elem)


@pytest.mark.parametrize(
"query,expected_matches",
[
("mang", ["mangeotter", "manger"]),
("Mang", ["mangeotter", "manger"]),
],
)
def test_get_verbs_that_start_with(query, expected_matches):
vp = VerbsParser()
matches = vp.get_verbs_that_start_with(query)
assert matches == expected_matches
vp = VerbsParser(Lang.fr)
verbs = vp.parse()
assert len(verbs) >= 7000
40 changes: 40 additions & 0 deletions tests/test_types/test_verbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from unittest.mock import patch

import pytest

from verbecc.src.parsers.verbs_parser import VerbsParser
from verbecc.src.defs.types.data.verbs import Verbs
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang


@pytest.fixture(scope="module")
def verbs():
vp = VerbsParser(Lang.fr)
verbs = vp.parse()
yield verbs


def test_verb(verbs):
verb = verbs.find_verb_by_infinitive("manger")
assert verb.infinitive == "manger"
assert verb.template == "man:ger"
assert verb.translation_en == "eat"


def test_verb_two(verbs):
verb = verbs.find_verb_by_infinitive("abattre")
assert verb.infinitive == "abattre"
assert verb.template == "bat:tre"
assert verb.translation_en == "tear down"


@pytest.mark.parametrize(
"query,expected_matches",
[
("mang", ["mangeotter", "manger"]),
("Mang", ["mangeotter", "manger"]),
],
)
def test_get_verbs_that_start_with(query, expected_matches, verbs):
matches = verbs.get_verbs_that_start_with(query)
assert matches == expected_matches
12 changes: 9 additions & 3 deletions verbecc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.person import Person, is_plural, is_singular
from verbecc.src.defs.types.partiple_inflection import ParticipleInflection
from verbecc.src.defs.types.participle_inflection import ParticipleInflection
from verbecc.src.defs.types.lang_code import LangCodeISO639_1
from verbecc.src.defs.types.mood import (
Mood,
Expand All @@ -37,8 +37,14 @@
from verbecc.src.defs.types.lang.es.voseo_options import VoseoOptions
import verbecc.src.defs.constants.localization as localization
import verbecc.src.defs.constants.grammar_defines as grammar_defines
from verbecc.src.parsers.mood_template import MoodTemplate
from verbecc.src.parsers.verb import Verb
from verbecc.src.defs.types.data.verb import Verb
from verbecc.src.defs.types.data.verbs import Verbs
from verbecc.src.defs.types.data.mood_template import MoodTemplate
from verbecc.src.defs.types.data.element import Element
from verbecc.src.defs.types.data.person_ending import PersonEnding
from verbecc.src.defs.types.data.tense_template import TenseTemplate
from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate
from verbecc.src.defs.types.data.conjugations import Conjugations
from verbecc.src.defs.types.exceptions import (
ConjugatorError,
ConjugationTemplateError,
Expand Down
4 changes: 2 additions & 2 deletions verbecc/src/conjugator/conjugation_object.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from verbecc.src.parsers.verb import Verb
from verbecc.src.parsers.conjugation_template import ConjugationTemplate
from verbecc.src.defs.types.data.verb import Verb
from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate


class ConjugationObjects:
Expand Down
21 changes: 10 additions & 11 deletions verbecc/src/conjugator/conjugator.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import logging

from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.mood import Mood
from verbecc.src.defs.types.tense import Tense
from verbecc.src.defs.types.conjugation import ConjugationInfo
from verbecc.src.defs.constants.config import DEVEL_MODE

logging_level = logging.CRITICAL + 1 # effectively disables logging
Expand All @@ -21,11 +17,12 @@
import copy
from typing import cast, List

from verbecc.src.parsers.verb import Verb
from verbecc.src.parsers.tense_template import TenseTemplate
from verbecc.src.parsers.conjugation_template import ConjugationTemplate
from verbecc.src.inflectors.inflector_factory import InflectorFactory
from verbecc.src.conjugator.conjugation_object import ConjugationObjects
from verbecc.src.defs.types.gender import Gender
from verbecc.src.defs.types.mood import Mood
from verbecc.src.defs.types.tense import Tense
from verbecc.src.defs.types.conjugation import ConjugationInfo
from verbecc.src.defs.types.data.verb import Verb
from verbecc.src.defs.types.person import Person
from verbecc.src.defs.types.exceptions import (
VerbNotFoundError,
Expand All @@ -42,10 +39,12 @@
from verbecc.src.defs.types.lang_specific_options import (
LangSpecificOptions,
)
from verbecc.src.conjugator.conjugation_object import ConjugationObjects
from verbecc.src.defs.types.lang_code import LangCodeISO639_1
from verbecc.src.utils.string_utils import strip_accents
from verbecc.src.defs.types.alternates_behavior import AlternatesBehavior
from verbecc.src.defs.types.data.tense_template import TenseTemplate
from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate
from verbecc.src.inflectors.inflector_factory import InflectorFactory
from verbecc.src.utils.string_utils import strip_accents


class Conjugator:
Expand Down Expand Up @@ -574,7 +573,7 @@ def _conjugate_simple_mood_tense(
if modify_stem_strip_accents and mood != self._inflector.get_infinitive_mood():
verb_stem = strip_accents(verb_stem)
ret: TenseConjugation = []
tense = tense_template.name
tense = tense_template.tense
compound = True
if (
tense in self._inflector.get_tenses_conjugated_without_pronouns()
Expand Down
2 changes: 1 addition & 1 deletion verbecc/src/defs/constants/grammar_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Dict, List, Tuple

from verbecc.src.defs.types.lang_code import LangCodeISO639_1
from verbecc.src.defs.types.partiple_inflection import ParticipleInflection
from verbecc.src.defs.types.participle_inflection import ParticipleInflection
from verbecc.src.defs.types.person import Person

# map of ISO 639-1 codes to long names (in target language)
Expand Down
2 changes: 1 addition & 1 deletion verbecc/src/defs/types/conjugation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

# PersonConjugation format depends on AlternatesBehavior
# With AlternatesBehavior.All, its a List[str]
# With AlternatesBehvioar.FirstOnly or SecondOnly, it's a str
# With AlternatesBehavior.FirstOnly or SecondOnly, it's a str
PersonConjugation = Union[List[str], str]
TenseConjugation = List[PersonConjugation]
MoodConjugation = Dict[Tense, TenseConjugation]
Expand Down
22 changes: 22 additions & 0 deletions verbecc/src/defs/types/data/conjugation_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import List

from verbecc.src.defs.types.data.element import Element
from verbecc.src.defs.types.data.mood_template import MoodTemplate
from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang


class ConjugationTemplate(Element):
def __init__(
self,
lang: Lang,
name: str,
mood_templates: List[MoodTemplate],
modify_stem: str = "",
) -> None:
self.lang = lang
self.name = name
self.mood_templates = mood_templates
self.modify_stem = modify_stem

def __repr__(self) -> str:
return f"lang={self.lang} name={self.name} mood_templates={self.mood_templates} modify_stem={self.modify_stem}"
Loading