• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apertium / apertium-apy / 15767027851

19 Jun 2025 10:07PM UTC coverage: 49.365%. First build
15767027851

Pull #247

github

Pull Request #247: bilsearch mode

261 of 756 branches covered (34.52%)

Branch coverage included in aggregate %.

19 of 69 new or added lines in 6 files covered. (27.54%)

1294 of 2394 relevant lines covered (54.05%)

0.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.53
/apertium_apy/mode_search.py
1
import re
1✔
2
import os
1✔
3
import logging
1✔
4
try:
1✔
5
    from lxml import etree
1✔
6
except ImportError:
×
7
    etree = None
×
8

9
from apertium_apy.utils import to_alpha3_code
1✔
10

11
if False:
1✔
12
    from typing import Dict, List, Tuple  # noqa: F401
13

14

15
def is_loop(dirpath, rootpath, real_root=None):
1✔
16
    if os.path.islink(dirpath):
1!
17
        # We just descended into a directory via a symbolic link
18
        # Check if we're referring to a directory that is
19
        # a parent of our nominal directory
20
        if not real_root:
×
21
            real_root = os.path.abspath(os.path.realpath(rootpath))
×
22
        relative = os.path.relpath(dirpath, rootpath)
×
23
        nominal_path = os.path.join(real_root, relative)
×
24
        real_path = os.path.abspath(os.path.realpath(dirpath))
×
25
        for nominal, real in zip(nominal_path.split(os.sep),
×
26
                                 real_path.split(os.sep)):
27
            if nominal != real:
×
28
                return False
×
29
        else:
30
            return True
×
31
    else:
32
        return False
1✔
33

34

35
def search_path(rootpath, include_pairs=True, verbosity=1):
1✔
36
    lang_code = r'[a-z]{2,3}(?:_[A-Za-z0-9]+)*'
1✔
37
    type_re = {
1✔
38
        'pair': re.compile(r'({0})-({0})\.mode'.format(lang_code)),
39
        'analyzer': re.compile(r'(({0}(-{0})?)-(an)?mor(ph)?)\.mode'.format(lang_code)),
40
        'generator': re.compile(r'(({0}(-{0})?)-gener[A-z]*)\.mode'.format(lang_code)),
41
        'tagger': re.compile(r'(({0}(-{0})?)-tagger)\.mode'.format(lang_code)),
42
        'spell': re.compile(r'(({0}(-{0})?)-spell)\.mode'.format(lang_code)),
43
        'tokenise': re.compile(r'(({0}(-{0})?)-tokenise)\.mode'.format(lang_code)),
44
        'guesser': re.compile(r'(({0}(-{0})?)-guess(er)?)\.mode'.format(lang_code)),
45
    }
46
    modes = {
47
        'pair': [],
1✔
48
        'analyzer': [],
49
        'generator': [],
50
        'tagger': [],
51
        'spell': [],
52
        'tokenise': [],
53
        'guesser': [],
54
    }  # type: Dict[str, List[Tuple[str, str, str]]]
55

56
    real_root = os.path.abspath(os.path.realpath(rootpath))
57

58
    for dirpath, dirnames, files in os.walk(rootpath, followlinks=True):
1✔
59
        if is_loop(dirpath, rootpath, real_root):
60
            dirnames[:] = []
1✔
61
            continue
1!
62
        for filename in [f for f in files if f.endswith('.mode')]:
×
63
            for mtype, regex in type_re.items():
×
64
                m = regex.match(filename)
1✔
65
                if m:
1✔
66
                    if mtype != 'pair':
1✔
67
                        modename = m.group(1)  # e.g. en-es-anmorph
1✔
68
                        langlist = [to_alpha3_code(x) for x in m.group(2).split('-')]
1!
NEW
69
                        lang_pair = '-'.join(langlist)  # e.g. en-es
×
NEW
70
                        dir_of_modes = os.path.dirname(dirpath)
×
NEW
71
                        mode = (dir_of_modes,
×
NEW
72
                                modename,
×
NEW
73
                                lang_pair)
×
NEW
74
                        modes[mtype].append(mode)
×
NEW
75
                    elif include_pairs:
×
76
                        lang_src = m.group(1)
1✔
77
                        lang_trg = m.group(2)
1✔
78
                        mode = (os.path.join(dirpath, filename),
1✔
79
                                to_alpha3_code(lang_src),
1✔
80
                                to_alpha3_code(lang_trg))
1✔
81
                        modes[mtype].append(mode)
1✔
82

1✔
83
    if verbosity > 1:
1!
84
        _log_modes(modes)
1✔
85

1✔
86
    return modes
1✔
87

88

89
def search_prefs(rootpath):
1✔
90
    if etree is None:
91
        logging.warning('Please install python3-lxml to enable /pairprefs endpoint')
1!
92
        return
1✔
93
    real_root = os.path.abspath(os.path.realpath(rootpath))
94
    prefspath = real_root + '/prefs'
1✔
95
    pairprefs = {}        # type: Dict[str, Dict[str, Dict[str, str]]]
96
    if not os.path.exists(prefspath):
97
        return pairprefs
1✔
98
    for f in os.listdir(prefspath):
1!
99
        fp = os.path.join(prefspath, f)
×
100
        try:
×
101
            mode = re.sub(r'[.]xml$', '', f)
1✔
102
            pairprefs[mode] = {pref.get('id'): {dsc.get('lang'): dsc.text
1✔
103
                               for dsc in pref.xpath('./description')}
1✔
104
                               for pref
1!
105
                               in etree.parse(fp).xpath('//preference')}
1✔
106
        except Exception:
×
107
            logging.warning('Exception on parsing preferences file {}'.format(fp))
×
108
    return pairprefs
×
109

×
110

×
111
def _log_modes(modes):
112
    """Print given modes to log."""
113
    for mtype in modes:
114
        if modes[mtype]:
×
115
            logging.info('"%s" modes found:\n%s', mtype, '\n'.join(['\t'.join(m) for m in modes[mtype]]))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc