CompileCommandExecutor._validate_locales()   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 19
rs 9.2333
c 0
b 0
f 0
cc 5
nop 2
1
import argparse
2
import sys
3
4
if sys.version_info >= (3, 9):
5
  List = list
6
else:
7
  from typing import List
8
9
from pathlib import Path
10
from typing import Optional
11
12
from .command_base import BaseCommand
13
from .command_base import BaseCommandExecutor
14
15
from .encoding import has_bom
16
17
from .gettext_tools import compile_translations
18
from .gettext_tools import validate_gettext_tools_exist
19
20
from .paths import get_names_of_immediate_subdirectories
21
from .paths import make_messages_dir_path
22
from .paths import make_mo_file_path
23
24
from .text import flatten_comma_separated_values
25
from .text import stringify_path
26
27
from .utils import halt
28
from .utils import print_err
29
from .utils import print_out
30
from .utils import show_usage_error_and_halt
31
32
from . import defaults
33
34
35
class CompileCommandExecutor(BaseCommandExecutor):
36
37
  def __init__(self, args=argparse.Namespace) -> None:
38
    self._locales_dir_path = self._handle_locales_dir_path(args.locales_dir)
39
    self._validate_locales_dir_path(self._locales_dir_path)
40
41
    self._locales = self._handle_locales(
42
      locales=args.locale,
43
      locales_dir_path=self._locales_dir_path,
44
    )
45
    self._validate_locales(
46
      locales=self._locales,
47
      locales_dir_path=self._locales_dir_path,
48
    )
49
50
    self._exclude = set(flatten_comma_separated_values(args.exclude))
51
52
    self._fuzzy = args.fuzzy
53
54
    self._msgfmt_extra_args = flatten_comma_separated_values(args.msgfmt_extra_args)
55
    self._verbose = args.verbose
56
57
  @staticmethod
58
  def _handle_locales_dir_path(path: str) -> Path:
59
    return Path(path).absolute()
60
61
  @staticmethod
62
  def _validate_locales_dir_path(path: Path) -> None:
63
    if not path.exists():
64
      print_err(f"locales dir does not exist (path={stringify_path(path)}")
65
      show_usage_error_and_halt()
66
67
    if not path.is_dir():
68
      print_err(f"locales dir is not a directory (path={stringify_path(path)})")
69
      show_usage_error_and_halt()
70
71
  @staticmethod
72
  def _handle_locales(
73
    locales: Optional[List[str]],
74
    locales_dir_path: Path,
75
  ) -> List[str]:
76
77
    if locales:
78
      return flatten_comma_separated_values(locales)
79
80
    return get_names_of_immediate_subdirectories(locales_dir_path)
81
82
  @staticmethod
83
  def _validate_locales(
84
    locales: List[str],
85
    locales_dir_path: Path,
86
  ) -> None:
87
    if not locales:
88
      print_err(
89
        "specify at least 1 locale or specify processing of all existing locales"
90
      )
91
      show_usage_error_and_halt()
92
93
    for locale in locales:
94
      path = make_messages_dir_path(locales_dir_path, locale)
95
      if not (path.exists() and path.is_dir()):
96
        print_err(
97
          f"invalid locale '{locale}': "
98
          f"directory '{stringify_path(path)}' expected to exist"
99
        )
100
        show_usage_error_and_halt()
101
102
  def __call__(self) -> None:
103
    validate_gettext_tools_exist()
104
105
    if self._verbose:
106
      self._print_input_args(
107
        locales_dir_path=stringify_path(self._locales_dir_path),
108
        locales=self._locales,
109
        fuzzy=self._fuzzy,
110
        msgfmt_extra_args=self._msgfmt_extra_args,
111
        verbose=self._verbose,
112
      )
113
114
    final_locales = sorted(set(self._locales) - self._exclude)
115
116
    for locale in final_locales:
117
      self._process_locale(locale=locale)
118
119
  def _process_locale(self, locale: str) -> None:
120
    if self._verbose:
121
      print_out(f"processing locale '{locale}'")
122
123
    messages_dir_path = make_messages_dir_path(self._locales_dir_path, locale)
124
125
    for path in messages_dir_path.iterdir():
126
      if path.is_file() and path.suffix == ".po":
127
        self._process_translations_file(file_path=path)
128
129
  def _process_translations_file(self, file_path: Path) -> None:
130
    if self._verbose:
131
      print_out(f"processing file '{stringify_path(file_path)}'")
132
133
    if has_bom(file_path):
134
      print_err(
135
        f"the file '{stringify_path(file_path)}' file has a BOM (Byte Order Mark). "
136
        f"Verboselib supports only '.po' files encoded in UTF-8 and without any BOM."
137
      )
138
      halt()
139
140
    mo_file_path = make_mo_file_path(file_path)
141
142
    compile_translations(
143
      mo_file_path=mo_file_path,
144
      po_file_path=file_path,
145
      fuzzy=self._fuzzy,
146
      msgfmt_extra_args=self._msgfmt_extra_args,
147
    )
148
149
150
class CompileCommand(BaseCommand):
151
  name = "compile"
152
  aliases = ["c", ]
153
  executor_class = CompileCommandExecutor
154
155
  @classmethod
156
  def make_parser(cls, factory=argparse.ArgumentParser) -> argparse.ArgumentParser:
157
    description = "compile '.po' text files into '.mo' binaries"
158
    parser = factory(
159
      prog=cls.name,
160
      description=description,
161
      add_help=True,
162
      help=description,
163
      formatter_class=argparse.ArgumentDefaultsHelpFormatter,
164
    )
165
    parser.add_argument(
166
      "-d", "--locale-dir",
167
      dest="locales_dir",
168
      default=defaults.DEFAULT_LOCALE_DIR_NAME,
169
      help="path to the directory where locales are stored",
170
    )
171
    parser.add_argument(
172
      "-l", "--locale",
173
      dest="locale",
174
      action="append",
175
      help=(
176
        "locale(s) to process, ex: 'en_US'; "
177
        "can be specified multiple times; "
178
        "all locales are processed if not specified"
179
      ),
180
    )
181
    parser.add_argument(
182
      "-e", "--exclude",
183
      dest="exclude",
184
      action="append",
185
      help="locale(s) to exclude, ex: 'en_US'; can be specified multiple times",
186
    )
187
    parser.add_argument(
188
      "-f", "--use-fuzzy",
189
      action="store_true",
190
      dest="fuzzy",
191
      default=False,
192
      help="use fuzzy translations",
193
    )
194
    parser.add_argument(
195
      "--msgfmt-extra-args",
196
      action="append",
197
      dest="msgfmt_extra_args",
198
      help=(
199
        "extra arguments for 'msgfmt' utility; "
200
        "can be comma-separated or specified multiple times"
201
      ),
202
    )
203
    parser.add_argument(
204
      "-v", "--verbose",
205
      action="store_true",
206
      dest="verbose",
207
      default=False,
208
      help="use verbose output",
209
    )
210
    return parser
211