Marco Ricci commited on 2024-12-25 21:41:22
Zeige 3 geänderte Dateien mit 801 Einfügungen und 0 Löschungen.
Extract all log messages into a separate module, so that they may be translated in a future version. We anticipate the use of such a message in a `logging` call, so we provide a wrapper object that defers the string interpolation until its serialization is called, and caches the result. (This is all as per the recommendation from the standard `logging` module.) Implemented this way, it is easy to run easy transformations on the specific log message template, e.g. substituting a translated message template, or trimming the filename placeholder if the filename is `None`. As an added benefit, this organization also makes it somewhat easy to ensure message consistency across different use sites and to generate diagnostics lists for inclusion in the manpage. While we intend to use the gettext toolset to implement the translation later, we do *not* currently use the standard `_` gettext alias or otherwise mark up the strings in an `xgettext`-compatible manner. Both the GNU version of `xgettext` and the Python work-alike `xgettext.py` suck: the GNU version does not completely understand Python syntax and cannot sensibly parse `black`-/`ruff`-formatted code using implicit string concatenation or trailing commas in function argument lists, and `xgettext.py` is so outdated (even in current Pythons) that it does not support `xgettext`'s `--add-comment` option for extracting extra comments for the translators. So instead of attempting to cram the strings and comments into some formatter-resistant and `xgettext`-compatible shape (a very futile endeavor so far), store enough information that we could generate the `.po` files ourselves with very little additional effort.
... | ... |
@@ -601,6 +601,189 @@ key or to type in a master passphrase. |
601 | 601 |
.Sh DIAGNOSTICS |
602 | 602 |
. |
603 | 603 |
.Ex -std "derivepassphrase vault" |
604 |
+.Pp |
|
605 |
+. |
|
606 |
+.Ss Fatal error messages on standard error |
|
607 |
+. |
|
608 |
+.Pq Li %s Ns No " indicates a variable part of the message." |
|
609 |
+. |
|
610 |
+.Bl -diag |
|
611 |
+. |
|
612 |
+.It %s is mutually exclusive with %s. |
|
613 |
+The two indicated options must not be used at the same time. |
|
614 |
+. |
|
615 |
+.It %s requires a SERVICE or \-\-config. |
|
616 |
+Using the indicated passphrase generation option requires the |
|
617 |
+.Ar SERVICE |
|
618 |
+argument or the |
|
619 |
+.Fl \-config |
|
620 |
+option. |
|
621 |
+. |
|
622 |
+.It %s requires a SERVICE. |
|
623 |
+Using the indicated option requires the |
|
624 |
+.Ar SERVICE |
|
625 |
+argument. |
|
626 |
+. |
|
627 |
+.It %s does not take a SERVICE argument. |
|
628 |
+The indicated option must not be specified together with the |
|
629 |
+.Ar SERVICE |
|
630 |
+argument. |
|
631 |
+. |
|
632 |
+.It Cannot load vault settings: %s. |
|
633 |
+There was a fatal problem loading the stored vault configuration data. |
|
634 |
+Further details are contained in the variable part of the message. |
|
635 |
+. |
|
636 |
+.It Cannot store vault settings: %s. |
|
637 |
+There was a fatal problem saving the vault configuration data. |
|
638 |
+Further details are contained in the variable part of the message. |
|
639 |
+. |
|
640 |
+.It Cannot import vault settings: %s. |
|
641 |
+There was a fatal problem loading the imported vault configuration data. |
|
642 |
+Further details are contained in the variable part of the message. |
|
643 |
+. |
|
644 |
+.It Cannot export vault settings: %s. |
|
645 |
+There was a fatal problem saving the exported vault configuration data. |
|
646 |
+Further details are contained in the variable part of the message. |
|
647 |
+. |
|
648 |
+.It Cannot load user config: %s. |
|
649 |
+There was a fatal problem loading the central user configuration file. |
|
650 |
+Further details are contained in the variable part of the message. |
|
651 |
+. |
|
652 |
+.It The user configuration file is invalid. |
|
653 |
+(Exactly what it says.) |
|
654 |
+. |
|
655 |
+.It No usable SSH keys were found |
|
656 |
+The running SSH agent does not contain any suitable SSH keys. |
|
657 |
+. |
|
658 |
+.It No valid SSH key selected |
|
659 |
+We requested that an SSH key be selected, but we got an invalid selection. |
|
660 |
+. |
|
661 |
+.It The requested SSH key is not loaded into the agent. |
|
662 |
+The running SSH agent does not contain the necessary SSH key. |
|
663 |
+. |
|
664 |
+.It Cannot find any running SSH agent because SSH_AUTH_SOCK is not set. |
|
665 |
+We require a running SSH agent, but cannot locate its communication channel, |
|
666 |
+which is normally indicated by the |
|
667 |
+.Ev SSH_AUTH_SOCK |
|
668 |
+environment variable. |
|
669 |
+. |
|
670 |
+.It Cannot connect to an SSH agent because this Python version does not support UNIX domain sockets. |
|
671 |
+This Python installation does not support the communication mechanism |
|
672 |
+necessary to talk to SSH agents. |
|
673 |
+. |
|
674 |
+.It Cannot connect to the SSH agent: %s. |
|
675 |
+We cannot connect to the SSH agent indicated by the |
|
676 |
+.Ev SSH_AUTH_SOCK |
|
677 |
+environment variable. |
|
678 |
+Further details are contained in the variable part of the message. |
|
679 |
+. |
|
680 |
+.It The SSH agent failed to complete the request |
|
681 |
+The SSH agent \(em while responsive in principle \(em failed to or refused |
|
682 |
+to supply a list of loaded keys. |
|
683 |
+. |
|
684 |
+.It Error communicating with the SSH agent |
|
685 |
+There was a system error communicating with the SSH agent. |
|
686 |
+. |
|
687 |
+.It Not saving any new notes: the user aborted the request. |
|
688 |
+(Exactly what it says.) |
|
689 |
+. |
|
690 |
+.It Cannot update %s settings without actual settings. |
|
691 |
+Using |
|
692 |
+.Fl \-config |
|
693 |
+requires at least one of the |
|
694 |
+.Fl \-phrase , \-key , \-length , No etc.\& |
|
695 |
+options. |
|
696 |
+. |
|
697 |
+.It Attempted to unset and set %s at the same time. |
|
698 |
+While handling |
|
699 |
+.Fl \-config , |
|
700 |
+the same configuration setting was passed as an option and as an argument to |
|
701 |
+.Fl \-unset . |
|
702 |
+. |
|
703 |
+.It Generating a passphrase requires a SERVICE. |
|
704 |
+(Exactly what it says.) |
|
705 |
+. |
|
706 |
+.It No passphrase or key was given in the configuration. |
|
707 |
+.Nm derivepassphrase vault |
|
708 |
+does not know whether to use a master SSH key or a master passphrase. |
|
709 |
+. |
|
710 |
+.It No passphrase was given: the user aborted the request. |
|
711 |
+(Exactly what it says.) |
|
712 |
+. |
|
713 |
+.It No SSH key was selected: the user aborted the request. |
|
714 |
+(Exactly what it says.) |
|
715 |
+. |
|
716 |
+.El |
|
717 |
+.Pp |
|
718 |
+. |
|
719 |
+.Ss Non-fatal warning and info messages on standard error |
|
720 |
+. |
|
721 |
+.Pq Li %s Ns No " indicates a variable part of the message." |
|
722 |
+. |
|
723 |
+.Bl -diag |
|
724 |
+. |
|
725 |
+.It The %s passphrase is not %s-normalized. |
|
726 |
+The indicated passphrase \(em as a Unicode string \(em is not properly |
|
727 |
+normalized according to the preferred Unicode normalization form |
|
728 |
+.Pq as specified in the central configuration file . |
|
729 |
+It is therefore possible that the passphrase \(em as a byte string \(em is |
|
730 |
+not the same byte string as you expect it to be |
|
731 |
+.Pq even though it Em looks No correct , |
|
732 |
+and that the derived passphrases thus do not match their expected values |
|
733 |
+either. |
|
734 |
+Please double-check. |
|
735 |
+. |
|
736 |
+.It An empty SERVICE is not supported by vault(1). |
|
737 |
+.Xr vault 1 |
|
738 |
+does not support the empty string as a value for |
|
739 |
+.Ar SERVICE ; |
|
740 |
+it will treat the |
|
741 |
+.Ar SERVICE |
|
742 |
+as missing. |
|
743 |
+For compatibility, |
|
744 |
+.Nm derivepassphrase vault |
|
745 |
+will do the same. |
|
746 |
+In particular, if the empty service is imported in a configuration via |
|
747 |
+.Fl \-import , |
|
748 |
+then this service cannot be accessed via the |
|
749 |
+.Nm derivepassphrase vault |
|
750 |
+command-line. |
|
751 |
+. |
|
752 |
+.It Replacing invalid value %s for key %s with %s. |
|
753 |
+When importing a configuration, the indicated invalid value has been |
|
754 |
+replaced with the indicated replacement value. |
|
755 |
+.Pq The Do interpretation Dc of the configuration doesn't change . |
|
756 |
+. |
|
757 |
+.It Removing ineffective setting %s = %s. |
|
758 |
+When importing a configuration, the indicated ineffective setting has been |
|
759 |
+removed. |
|
760 |
+.Pq The Do interpretation Dc of the configuration doesn't change . |
|
761 |
+. |
|
762 |
+.It Setting a %s passphrase is ineffective because a key is also set. |
|
763 |
+The configuration (global or key-specific) contains both a stored master |
|
764 |
+passphrase and an SSH key. |
|
765 |
+The master passphrase will not take effect. |
|
766 |
+. |
|
767 |
+.It A subcommand will be required in v1.0. |
|
768 |
+.Bo |
|
769 |
+Since v0.2.0, until v1.0. |
|
770 |
+.Bc |
|
771 |
+This command now requires a subcommand. |
|
772 |
+For compatibility, it currently defaults to |
|
773 |
+.Dq vault . |
|
774 |
+. |
|
775 |
+.It Using deprecated v0.1-style config file %s, instead of v0.2-style %s. |
|
776 |
+.Bo |
|
777 |
+Since v0.2.0, until v1.0. |
|
778 |
+.Bc |
|
779 |
+A configuration file has been renamed. |
|
780 |
+.Nm derivepassphrase vault |
|
781 |
+will attempt to rename the file itself |
|
782 |
+.Pq Qq Li Successfully migrated to %s. , |
|
783 |
+or complain if it cannot rename it |
|
784 |
+.Pq Qq Li Failed to migrate to %s: %s . |
|
785 |
+. |
|
786 |
+.El |
|
604 | 787 |
. |
605 | 788 |
.Sh COMPATIBILITY |
606 | 789 |
. |
... | ... |
@@ -240,6 +240,159 @@ This is a property specific to the key type, and sometimes the agent used: |
240 | 240 |
|
241 | 241 |
The derivepassphrase vault utility exits 0 on success, and >0 if an error occurs. |
242 | 242 |
|
243 |
+### Fatal error messsages on standard error |
|
244 |
+ |
|
245 |
+(`%s` indicates a variable part of the message.) |
|
246 |
+ |
|
247 |
+??? failure "`%s is mutually exclusive with %s.`" |
|
248 |
+ |
|
249 |
+ The two indicated options must not be used at the same time. |
|
250 |
+ |
|
251 |
+??? failure "`%s requires a SERVICE or --config.`" |
|
252 |
+ |
|
253 |
+ Using the indicated passphrase generation option requires the <var>SERVICE</var> argument or the `--config` option. |
|
254 |
+ |
|
255 |
+??? failure "`%s requires a SERVICE.`" |
|
256 |
+ |
|
257 |
+ Using the indicated option requires the <var>SERVICE</var> argument. |
|
258 |
+ |
|
259 |
+??? failure "`%s does not take a SERVICE argument.`" |
|
260 |
+ |
|
261 |
+ The indicated option must not be specified together with the <var>SERVICE</var> argument. |
|
262 |
+ |
|
263 |
+??? failure "`Cannot load vault settings: %s.`" |
|
264 |
+ |
|
265 |
+ There was a fatal problem loading the stored vault configuration data. |
|
266 |
+ Further details are contained in the variable part of the message. |
|
267 |
+ |
|
268 |
+??? failure "`Cannot store vault settings: %s.`" |
|
269 |
+ |
|
270 |
+ There was a fatal problem saving the vault configuration data. |
|
271 |
+ Further details are contained in the variable part of the message. |
|
272 |
+ |
|
273 |
+??? failure "`Cannot import vault settings: %s.`" |
|
274 |
+ |
|
275 |
+ There was a fatal problem loading the imported vault configuration data. |
|
276 |
+ Further details are contained in the variable part of the message. |
|
277 |
+ |
|
278 |
+??? failure "`Cannot export vault settings: %s.`" |
|
279 |
+ |
|
280 |
+ There was a fatal problem saving the exported vault configuration data. |
|
281 |
+ Further details are contained in the variable part of the message. |
|
282 |
+ |
|
283 |
+??? failure "`Cannot load user config: %s.`" |
|
284 |
+ |
|
285 |
+ There was a fatal problem loading the central user configuration file. |
|
286 |
+ Further details are contained in the variable part of the message. |
|
287 |
+ |
|
288 |
+??? failure "`The user configuration file is invalid.`" |
|
289 |
+ |
|
290 |
+ (Exactly what it says.) |
|
291 |
+ |
|
292 |
+??? failure "`No usable SSH keys were found`" |
|
293 |
+ |
|
294 |
+ The running SSH agent does not contain any suitable SSH keys. |
|
295 |
+ |
|
296 |
+??? failure "`No valid SSH key selected`" |
|
297 |
+ |
|
298 |
+ We requested that an SSH key be selected, but we got an invalid selection. |
|
299 |
+ |
|
300 |
+??? failure "`The requested SSH key is not loaded into the agent.`" |
|
301 |
+ |
|
302 |
+ The running SSH agent does not contain the necessary SSH key. |
|
303 |
+ |
|
304 |
+??? failure "`Cannot find any running SSH agent because SSH_AUTH_SOCK is not set.`" |
|
305 |
+ |
|
306 |
+ We require a running SSH agent, but cannot locate its communication channel, which is normally indicated by the `SSH_AUTH_SOCK` environment variable. |
|
307 |
+ |
|
308 |
+??? failure "`Cannot connect to an SSH agent because this Python version does not support UNIX domain sockets.`" |
|
309 |
+ |
|
310 |
+ This Python installation does not support the communication mechanism necessary to talk to SSH agents. |
|
311 |
+ |
|
312 |
+??? failure "`Cannot connect to the SSH agent: %s.`" |
|
313 |
+ |
|
314 |
+ We cannot connect to the SSH agent indicated by the `SSH_AUTH_SOCK` environment variable. |
|
315 |
+ Further details are contained in the variable part of the message. |
|
316 |
+ |
|
317 |
+??? failure "`The SSH agent failed to complete the request`" |
|
318 |
+ |
|
319 |
+ The SSH agent---while responsive in principle---failed to or refused to supply a list of loaded keys. |
|
320 |
+ |
|
321 |
+??? failure "`Error communicating with the SSH agent`" |
|
322 |
+ |
|
323 |
+ There was a system error communicating with the SSH agent. |
|
324 |
+ |
|
325 |
+??? failure "`Not saving any new notes: the user aborted the request.`" |
|
326 |
+ |
|
327 |
+ (Exactly what it says.) |
|
328 |
+ |
|
329 |
+??? failure "`Cannot update %s settings without actual settings.`" |
|
330 |
+ |
|
331 |
+ Using `--config` requires at least one of the `--phrase`, `--key`, `--length`, etc. options. |
|
332 |
+ |
|
333 |
+??? failure "`Attempted to unset and set %s at the same time.`" |
|
334 |
+ |
|
335 |
+ While handling `--config`, the same configuration setting was passed as an option and as an argument to `--unset`. |
|
336 |
+ |
|
337 |
+??? failure "`Generating a passphrase requires a SERVICE.`" |
|
338 |
+ |
|
339 |
+ (Exactly what it says.) |
|
340 |
+ |
|
341 |
+??? failure "`No passphrase or key was given in the configuration.`" |
|
342 |
+ |
|
343 |
+ <b>derivepassphrase vault</b> does not know whether to use a master SSH key or a master passphrase. |
|
344 |
+ |
|
345 |
+??? failure "`No passphrase was given: the user aborted the request.`" |
|
346 |
+ |
|
347 |
+ (Exactly what it says.) |
|
348 |
+ |
|
349 |
+??? failure "`No SSH key was selected: the user aborted the request.`" |
|
350 |
+ |
|
351 |
+ (Exactly what it says.) |
|
352 |
+ |
|
353 |
+### Non-fatal warning and info messages on standard error |
|
354 |
+ |
|
355 |
+(`%s` indicates a variable part of the message.) |
|
356 |
+ |
|
357 |
+??? warning "`The %s passphrase is not %s-normalized.`" |
|
358 |
+ |
|
359 |
+ The indicated passphrase---as a Unicode string---is not properly normalized according to the preferred Unicode normalization form (as specified in the central configuration file). |
|
360 |
+ It is therefore possible that the passphrase---as a byte string---is not the same byte string as you expect it to be (even though it *looks* correct), and that the derived passphrases thus do not match their expected values either. |
|
361 |
+ Please double-check. |
|
362 |
+ |
|
363 |
+??? warning "`An empty SERVICE is not supported by vault(1).`" |
|
364 |
+ |
|
365 |
+ <i>vault</i>(1) does not support the empty string as a value for <var>SERVICE</var>; it will treat the <var>SERVICE</var> as missing. |
|
366 |
+ For compatibility, <b>derivepassphrase vault</b> will do the same. |
|
367 |
+ In particular, if the empty service is imported in a configuration via `--import`, then this service cannot be accessed via the <b>derivepassphrase vault</b> command-line. |
|
368 |
+ |
|
369 |
+??? warning "`Replacing invalid value %s for key %s with %s.`" |
|
370 |
+ |
|
371 |
+ When importing a configuration, the indicated invalid value has been replaced with the indicated replacement value. |
|
372 |
+ (The "interpretation" of the configuration doesn’t change). |
|
373 |
+ |
|
374 |
+??? warning "`Removing ineffective setting %s = %s.`" |
|
375 |
+ |
|
376 |
+ When importing a configuration, the indicated ineffective setting has been removed. |
|
377 |
+ (The "interpretation" of the configuration doesn’t change). |
|
378 |
+ |
|
379 |
+??? warning "`Setting a %s passphrase is ineffective because a key is also set.`" |
|
380 |
+ |
|
381 |
+ The configuration (global or key-specific) contains both a stored master passphrase and an SSH key. |
|
382 |
+ The master passphrase will not take effect. |
|
383 |
+ |
|
384 |
+??? warning "`A subcommand will be required in v1.0.`" |
|
385 |
+ |
|
386 |
+ [Since v0.2.0, until v1.0.] |
|
387 |
+ This command now requires a subcommand. |
|
388 |
+ For compatibility, it currently defaults to "vault". |
|
389 |
+ |
|
390 |
+??? warning "`Using deprecated v0.1-style config file %s, instead of v0.2-style %s.`" |
|
391 |
+ |
|
392 |
+ [Since v0.2.0, until v1.0.] |
|
393 |
+ A configuration file has been renamed. |
|
394 |
+ <b>derivepassphrase vault</b> will attempt to rename the file itself (`Successfully migrated to %s.`), or complain if it cannot rename it (`Failed to migrate to %s: %s`). |
|
395 |
+ |
|
243 | 396 |
## COMPATIBILITY |
244 | 397 |
|
245 | 398 |
### With other software |
... | ... |
@@ -0,0 +1,465 @@ |
1 |
+# SPDX-FileCopyrightText: 2024 Marco Ricci <software@the13thletter.info> |
|
2 |
+# |
|
3 |
+# SPDX-Licence-Identifier: MIT |
|
4 |
+ |
|
5 |
+"""Internal module. Do not use. Contains error strings and functions.""" |
|
6 |
+ |
|
7 |
+from __future__ import annotations |
|
8 |
+ |
|
9 |
+import enum |
|
10 |
+import gettext |
|
11 |
+import inspect |
|
12 |
+import types |
|
13 |
+from typing import TYPE_CHECKING, NamedTuple |
|
14 |
+ |
|
15 |
+import derivepassphrase as dpp |
|
16 |
+ |
|
17 |
+if TYPE_CHECKING: |
|
18 |
+ from collections.abc import Iterable, Mapping |
|
19 |
+ |
|
20 |
+ from typing_extensions import Any, Self |
|
21 |
+ |
|
22 |
+__author__ = dpp.__author__ |
|
23 |
+__version__ = dpp.__version__ |
|
24 |
+ |
|
25 |
+__all__ = ('PROG_NAME',) |
|
26 |
+ |
|
27 |
+PROG_NAME = 'derivepassphrase' |
|
28 |
+translation = gettext.translation(PROG_NAME, fallback=True) |
|
29 |
+ |
|
30 |
+ |
|
31 |
+class TranslatableString(NamedTuple): |
|
32 |
+ singular: str |
|
33 |
+ plural: str |
|
34 |
+ l10n_context: str |
|
35 |
+ translator_comments: str |
|
36 |
+ flags: frozenset[str] |
|
37 |
+ |
|
38 |
+ |
|
39 |
+def _prepare_translatable( |
|
40 |
+ msg: str, |
|
41 |
+ comments: str = '', |
|
42 |
+ context: str = '', |
|
43 |
+ plural_msg: str = '', |
|
44 |
+ *, |
|
45 |
+ flags: Iterable[str] = (), |
|
46 |
+) -> TranslatableString: |
|
47 |
+ msg = inspect.cleandoc(msg) |
|
48 |
+ plural_msg = inspect.cleandoc(plural_msg) |
|
49 |
+ context = context.strip() |
|
50 |
+ comments = inspect.cleandoc(comments) |
|
51 |
+ flags = ( |
|
52 |
+ frozenset(f.strip() for f in flags) |
|
53 |
+ if not isinstance(flags, str) |
|
54 |
+ else frozenset({flags}) |
|
55 |
+ ) |
|
56 |
+ assert ( |
|
57 |
+ '{' not in msg |
|
58 |
+ or bool(flags & {'python-brace-format', 'no-python-brace-format'}) |
|
59 |
+ ), f'Missing flag for how to deal with brace in {msg!r}' |
|
60 |
+ assert ( |
|
61 |
+ '%' not in msg |
|
62 |
+ or bool(flags & {'python-format', 'no-python-format'}) |
|
63 |
+ ), f'Missing flag for how to deal with percent character in {msg!r}' |
|
64 |
+ return TranslatableString(msg, plural_msg, context, comments, flags) |
|
65 |
+ |
|
66 |
+ |
|
67 |
+class LogObject: |
|
68 |
+ |
|
69 |
+ def __init__( |
|
70 |
+ self, |
|
71 |
+ template: TranslatableString, |
|
72 |
+ args_dict: Mapping[str, Any] = types.MappingProxyType({}), |
|
73 |
+ /, |
|
74 |
+ **kwargs: Any, # noqa: ANN401 |
|
75 |
+ ) -> None: |
|
76 |
+ self.template = template |
|
77 |
+ self.kwargs = {**args_dict, **kwargs} |
|
78 |
+ self._rendered: str | None = None |
|
79 |
+ |
|
80 |
+ def __str__(self) -> str: |
|
81 |
+ if self._rendered is None: |
|
82 |
+ context = self.template.l10n_context |
|
83 |
+ template = self.template.singular |
|
84 |
+ if context is not None: |
|
85 |
+ template = translation.pgettext(context, template) |
|
86 |
+ else: |
|
87 |
+ template = translation.gettext(template) |
|
88 |
+ self._rendered = template.format(**self.kwargs) |
|
89 |
+ return self._rendered |
|
90 |
+ |
|
91 |
+ def maybe_without_filename(self) -> Self: |
|
92 |
+ if ( |
|
93 |
+ self.kwargs.get('filename') is None |
|
94 |
+ and ': {filename!r}' in self.template.singular |
|
95 |
+ ): |
|
96 |
+ singular = ''.join( |
|
97 |
+ self.template.singular.split(': {filename!r}', 1) |
|
98 |
+ ) |
|
99 |
+ plural = ( |
|
100 |
+ ''.join(self.template.plural.split(': {filename!r}', 1)) |
|
101 |
+ if self.template.plural |
|
102 |
+ else self.template.plural |
|
103 |
+ ) |
|
104 |
+ return self.__class__( |
|
105 |
+ self.template._replace(singular=singular, plural=plural), |
|
106 |
+ self.kwargs, |
|
107 |
+ ) |
|
108 |
+ return self |
|
109 |
+ |
|
110 |
+ @classmethod |
|
111 |
+ def InfoMsg( # noqa: N802 |
|
112 |
+ cls, |
|
113 |
+ msg_template: InfoMsgTemplate, |
|
114 |
+ args_dict: Mapping[str, Any] = types.MappingProxyType({}), |
|
115 |
+ /, |
|
116 |
+ **kwargs: Any, # noqa: ANN401 |
|
117 |
+ ) -> Self: |
|
118 |
+ return cls(msg_template.value, {**args_dict, **kwargs}) |
|
119 |
+ |
|
120 |
+ @classmethod |
|
121 |
+ def WarnMsg( # noqa: N802 |
|
122 |
+ cls, |
|
123 |
+ msg_template: WarnMsgTemplate, |
|
124 |
+ args_dict: Mapping[str, Any] = types.MappingProxyType({}), |
|
125 |
+ /, |
|
126 |
+ **kwargs: Any, # noqa: ANN401 |
|
127 |
+ ) -> Self: |
|
128 |
+ return cls(msg_template.value, {**args_dict, **kwargs}) |
|
129 |
+ |
|
130 |
+ @classmethod |
|
131 |
+ def ErrMsg( # noqa: N802 |
|
132 |
+ cls, |
|
133 |
+ msg_template: ErrMsgTemplate, |
|
134 |
+ args_dict: Mapping[str, Any] = types.MappingProxyType({}), |
|
135 |
+ /, |
|
136 |
+ **kwargs: Any, # noqa: ANN401 |
|
137 |
+ ) -> Self: |
|
138 |
+ return cls(msg_template.value, {**args_dict, **kwargs}) |
|
139 |
+ |
|
140 |
+ |
|
141 |
+class InfoMsgTemplate(enum.Enum): |
|
142 |
+ CANNOT_LOAD_AS_VAULT_CONFIG = _prepare_translatable( |
|
143 |
+ comments=r""" |
|
144 |
+ TRANSLATORS: "fmt" is a string such as "v0.2" or "storeroom", |
|
145 |
+ indicating the format which we tried to load the vault |
|
146 |
+ configuration as. |
|
147 |
+ """, |
|
148 |
+ msg='Cannot load {path!r} as a {fmt!s} vault configuration.', |
|
149 |
+ context='info message', |
|
150 |
+ flags='python-brace-format', |
|
151 |
+ ) |
|
152 |
+ PIP_INSTALL_EXTRA = _prepare_translatable( |
|
153 |
+ comments=r""" |
|
154 |
+ TRANSLATORS: This message immediately follows an error message |
|
155 |
+ about a missing library that needs to be installed. The Python |
|
156 |
+ Package Index (PyPI) supports declaring sets of optional |
|
157 |
+ dependencies as "extras", so users installing from PyPI can |
|
158 |
+ request reinstallation with a named "extra" being enabled. This |
|
159 |
+ would then let the installer take care of the missing libraries |
|
160 |
+ automatically, hence this suggestion to PyPI users. |
|
161 |
+ """, |
|
162 |
+ msg='(For users installing from PyPI, see the {extra_name!r} extra.)', |
|
163 |
+ context='info message', |
|
164 |
+ flags='python-brace-format', |
|
165 |
+ ) |
|
166 |
+ SUCCESSFULLY_MIGRATED = _prepare_translatable( |
|
167 |
+ comments=r""" |
|
168 |
+ TRANSLATORS: This info message immediately follows the "Using |
|
169 |
+ deprecated v0.1-style ..." deprecation warning. |
|
170 |
+ """, |
|
171 |
+ msg='Successfully migrated to {path!r}.', |
|
172 |
+ context='info message', |
|
173 |
+ flags='python-brace-format', |
|
174 |
+ ) |
|
175 |
+ |
|
176 |
+ |
|
177 |
+class WarnMsgTemplate(enum.Enum): |
|
178 |
+ EMPTY_SERVICE_NOT_SUPPORTED = _prepare_translatable( |
|
179 |
+ 'An empty SERVICE is not supported by vault(1). ' |
|
180 |
+ 'For compatibility, this will be treated as if SERVICE was not ' |
|
181 |
+ 'supplied, i.e., it will error out, or operate on global settings.', |
|
182 |
+ comments='', |
|
183 |
+ context='warning message', |
|
184 |
+ ) |
|
185 |
+ EMPTY_SERVICE_SETTINGS_INACCESSIBLE = _prepare_translatable( |
|
186 |
+ f'An empty SERVICE is not supported by vault(1). ' |
|
187 |
+ f'The empty-string service settings will be ' |
|
188 |
+ f'inaccessible and ineffective. ' |
|
189 |
+ f'To ensure that vault(1) and {PROG_NAME!s} see the settings, ' |
|
190 |
+ f'move them into the "global" section.', |
|
191 |
+ comments='', |
|
192 |
+ context='warning message', |
|
193 |
+ ) |
|
194 |
+ FAILED_TO_MIGRATE_CONFIG = _prepare_translatable( |
|
195 |
+ comments=r""" |
|
196 |
+ TRANSLATORS: The error message is usually supplied by the |
|
197 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
198 |
+ """, |
|
199 |
+ msg='Failed to migrate to {path!r}: {error!s}: {filename!r}.', |
|
200 |
+ context='warning message', |
|
201 |
+ flags='python-brace-format', |
|
202 |
+ ) |
|
203 |
+ GLOBAL_PASSPHRASE_INEFFECTIVE = _prepare_translatable( |
|
204 |
+ 'Setting a global passphrase is ineffective ' |
|
205 |
+ 'because a key is also set.', |
|
206 |
+ comments='', |
|
207 |
+ context='warning message', |
|
208 |
+ ) |
|
209 |
+ PASSPHRASE_NOT_NORMALIZED = _prepare_translatable( |
|
210 |
+ comments=r""" |
|
211 |
+ TRANSLATORS: The key is a (vault) configuration key, in JSONPath |
|
212 |
+ syntax, typically "$.global" for the global passphrase or |
|
213 |
+ "$.services.service_name" or "$.services["service with spaces"]" |
|
214 |
+ for the services "service_name" and "service with spaces", |
|
215 |
+ respectively. The form is one of the four Unicode normalization |
|
216 |
+ forms: NFC, NFD, NFKC, NFKD. |
|
217 |
+ |
|
218 |
+ The asterisks are not special. Please feel free to substitute |
|
219 |
+ any other appropriate way to mark up emphasis of the word |
|
220 |
+ "displays". |
|
221 |
+ """, |
|
222 |
+ msg='The {key!s} passphrase is not {form!s}-normalized. ' |
|
223 |
+ 'Its serialization as a byte string may not be what you ' |
|
224 |
+ 'expect it to be, even if it *displays* correctly. ' |
|
225 |
+ 'Please make sure to double-check any derived ' |
|
226 |
+ 'passphrases for unexpected results.', |
|
227 |
+ context='warning message', |
|
228 |
+ flags='python-brace-format', |
|
229 |
+ ) |
|
230 |
+ SERVICE_PASSPHRASE_INEFFECTIVE = _prepare_translatable( |
|
231 |
+ comments=r""" |
|
232 |
+ TRANSLATORS: The key that is set need not necessarily be set at |
|
233 |
+ the service level; it may be a global key as well. |
|
234 |
+ """, |
|
235 |
+ msg='Setting a service passphrase is ineffective ' |
|
236 |
+ 'because a key is also set: {service!s}.', |
|
237 |
+ context='warning message', |
|
238 |
+ flags='python-brace-format', |
|
239 |
+ ) |
|
240 |
+ STEP_REMOVE_INEFFECTIVE_VALUE = _prepare_translatable( |
|
241 |
+ 'Removing ineffective setting {path!s} = {old!s}.', |
|
242 |
+ comments='', |
|
243 |
+ context='warning message', |
|
244 |
+ flags='python-brace-format', |
|
245 |
+ ) |
|
246 |
+ STEP_REPLACE_INVALID_VALUE = _prepare_translatable( |
|
247 |
+ 'Replacing invalid value {old!s} for key {path!s} with {new!s}.', |
|
248 |
+ comments='', |
|
249 |
+ context='warning message', |
|
250 |
+ flags='python-brace-format', |
|
251 |
+ ) |
|
252 |
+ V01_STYLE_CONFIG = _prepare_translatable( |
|
253 |
+ 'Using deprecated v0.1-style config file {old!r}, ' |
|
254 |
+ 'instead of v0.2-style {new!r}. ' |
|
255 |
+ 'Support for v0.1-style config filenames will be removed in v1.0.', |
|
256 |
+ comments='', |
|
257 |
+ context='deprecation warning message', |
|
258 |
+ flags='python-brace-format', |
|
259 |
+ ) |
|
260 |
+ V10_SUBCOMMAND_REQUIRED = _prepare_translatable( |
|
261 |
+ comments=r""" |
|
262 |
+ TRANSLATORS: This deprecation warning may be issued at any |
|
263 |
+ level, i.e. we may actually be talking about subcommands, or |
|
264 |
+ sub-subcommands, or sub-sub-subcommands, etc., which is what the |
|
265 |
+ "here" is supposed to indicate. |
|
266 |
+ """, |
|
267 |
+ msg='A subcommand will be required here in v1.0. ' |
|
268 |
+ 'See --help for available subcommands. ' |
|
269 |
+ 'Defaulting to subcommand "vault".', |
|
270 |
+ context='deprecation warning message', |
|
271 |
+ ) |
|
272 |
+ |
|
273 |
+ |
|
274 |
+class ErrMsgTemplate(enum.Enum): |
|
275 |
+ CANNOT_CONNECT_TO_AGENT = _prepare_translatable( |
|
276 |
+ comments=r""" |
|
277 |
+ TRANSLATORS: The error message is usually supplied by the |
|
278 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
279 |
+ """, |
|
280 |
+ msg='Cannot connect to the SSH agent: {error!s}: {filename!r}.', |
|
281 |
+ context='error message', |
|
282 |
+ flags='python-brace-format', |
|
283 |
+ ) |
|
284 |
+ CANNOT_DECODEIMPORT_VAULT_SETTINGS = _prepare_translatable( |
|
285 |
+ msg='Cannot import vault settings: cannot decode JSON: {error!s}.', |
|
286 |
+ comments='', |
|
287 |
+ context='error message', |
|
288 |
+ flags='python-brace-format', |
|
289 |
+ ) |
|
290 |
+ CANNOT_EXPORT_VAULT_SETTINGS = _prepare_translatable( |
|
291 |
+ comments=r""" |
|
292 |
+ TRANSLATORS: The error message is usually supplied by the |
|
293 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
294 |
+ """, |
|
295 |
+ msg='Cannot export vault settings: {error!s}: {filename!r}.', |
|
296 |
+ context='error message', |
|
297 |
+ flags='python-brace-format', |
|
298 |
+ ) |
|
299 |
+ CANNOT_IMPORT_VAULT_SETTINGS = _prepare_translatable( |
|
300 |
+ comments=r""" |
|
301 |
+ TRANSLATORS: The error message is usually supplied by the |
|
302 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
303 |
+ """, |
|
304 |
+ msg='Cannot import vault settings: {error!s}: {filename!r}.', |
|
305 |
+ context='error message', |
|
306 |
+ flags='python-brace-format', |
|
307 |
+ ) |
|
308 |
+ CANNOT_LOAD_USER_CONFIG = _prepare_translatable( |
|
309 |
+ comments=r""" |
|
310 |
+ TRANSLATORS: The error message is usually supplied by the |
|
311 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
312 |
+ """, |
|
313 |
+ msg='Cannot load user config: {error!s}: {filename!r}.', |
|
314 |
+ context='error message', |
|
315 |
+ flags='python-brace-format', |
|
316 |
+ ) |
|
317 |
+ CANNOT_LOAD_VAULT_SETTINGS = _prepare_translatable( |
|
318 |
+ comments=r""" |
|
319 |
+ TRANSLATORS: The error message is usually supplied by the |
|
320 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
321 |
+ """, |
|
322 |
+ msg='Cannot load vault settings: {error!s}: {filename!r}.', |
|
323 |
+ context='error message', |
|
324 |
+ flags='python-brace-format', |
|
325 |
+ ) |
|
326 |
+ CANNOT_PARSE_AS_VAULT_CONFIG = _prepare_translatable( |
|
327 |
+ comments=r""" |
|
328 |
+ TRANSLATORS: Unlike the "Cannot load {path!r} as a {fmt!s} vault |
|
329 |
+ configuration." message, *this* error message is emitted when we |
|
330 |
+ have tried loading the path in each of our supported formats, |
|
331 |
+ and failed. The user will thus see the above "Cannot load ..." |
|
332 |
+ warning message potentially multiple times, and this error |
|
333 |
+ message at the very bottom. |
|
334 |
+ """, |
|
335 |
+ msg='Cannot parse {path!r} as a valid vault-native ' |
|
336 |
+ 'configuration file/directory.', |
|
337 |
+ context='error message', |
|
338 |
+ flags='python-brace-format', |
|
339 |
+ ) |
|
340 |
+ CANNOT_STORE_VAULT_SETTINGS = _prepare_translatable( |
|
341 |
+ comments=r""" |
|
342 |
+ TRANSLATORS: The error message is usually supplied by the |
|
343 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
344 |
+ """, |
|
345 |
+ msg='Cannot store vault settings: {error!s}: {filename!r}.', |
|
346 |
+ context='error message', |
|
347 |
+ flags='python-brace-format', |
|
348 |
+ ) |
|
349 |
+ CANNOT_UPDATE_SETTINGS_NO_SETTINGS = _prepare_translatable( |
|
350 |
+ msg='Cannot update {settings_type!s} settings ' |
|
351 |
+ 'without any given settings. ' |
|
352 |
+ 'You must specify at least one of --lower, ..., ' |
|
353 |
+ '--symbol, or --phrase or --key.', |
|
354 |
+ comments='', |
|
355 |
+ context='error message', |
|
356 |
+ flags='python-brace-format', |
|
357 |
+ ) |
|
358 |
+ INVALID_VAULT_CONFIG = _prepare_translatable( |
|
359 |
+ comments=r""" |
|
360 |
+ TRANSLATORS: This error message is a reaction to a validator |
|
361 |
+ function saying *that* the configuration is not valid, but not |
|
362 |
+ *how* it is not valid. The configuration file is principally |
|
363 |
+ parsable, however. |
|
364 |
+ """, |
|
365 |
+ msg='Invalid vault config: {config!r}.', |
|
366 |
+ context='error message', |
|
367 |
+ flags='python-brace-format', |
|
368 |
+ ) |
|
369 |
+ MISSING_MODULE = _prepare_translatable( |
|
370 |
+ 'Cannot load the required Python module {module!r}.', |
|
371 |
+ comments='', |
|
372 |
+ context='error message', |
|
373 |
+ flags='python-brace-format', |
|
374 |
+ ) |
|
375 |
+ NO_AF_UNIX = _prepare_translatable( |
|
376 |
+ 'Cannot connect to an SSH agent because this Python version ' |
|
377 |
+ 'does not support UNIX domain sockets.', |
|
378 |
+ comments='', |
|
379 |
+ context='error message', |
|
380 |
+ ) |
|
381 |
+ NO_KEY_OR_PHRASE = _prepare_translatable( |
|
382 |
+ 'No passphrase or key was given in the configuration. ' |
|
383 |
+ 'In this case, the --phrase or --key argument is required.', |
|
384 |
+ comments='', |
|
385 |
+ context='error message', |
|
386 |
+ ) |
|
387 |
+ NO_SSH_AGENT_FOUND = _prepare_translatable( |
|
388 |
+ 'Cannot find any running SSH agent because SSH_AUTH_SOCK is not set.', |
|
389 |
+ comments='', |
|
390 |
+ context='error message', |
|
391 |
+ ) |
|
392 |
+ PARAMS_MUTUALLY_EXCLUSIVE = _prepare_translatable( |
|
393 |
+ comments=r""" |
|
394 |
+ TRANSLATORS: The params are long-form command-line option names. |
|
395 |
+ Typical example: "--key is mutually exclusive with --phrase." |
|
396 |
+ """, |
|
397 |
+ msg='{param1!s} is mutually exclusive with {param2!s}.', |
|
398 |
+ context='error message', |
|
399 |
+ flags='python-brace-format', |
|
400 |
+ ) |
|
401 |
+ PARAMS_NEEDS_SERVICE_OR_CONFIG = _prepare_translatable( |
|
402 |
+ comments=r""" |
|
403 |
+ TRANSLATORS: The param is a long-form command-line option name, |
|
404 |
+ and "SERVICE" is the command-line argument for the (sometimes |
|
405 |
+ optional) service name. |
|
406 |
+ """, |
|
407 |
+ msg='{param!s} requires a SERVICE or --config.', |
|
408 |
+ context='error message', |
|
409 |
+ flags='python-brace-format', |
|
410 |
+ ) |
|
411 |
+ PARAMS_NEEDS_SERVICE = _prepare_translatable( |
|
412 |
+ comments=r""" |
|
413 |
+ TRANSLATORS: The param is a long-form command-line option name, |
|
414 |
+ and "SERVICE" is the command-line argument for the (sometimes |
|
415 |
+ optional) service name. |
|
416 |
+ """, |
|
417 |
+ msg='{param!s} requires a SERVICE.', |
|
418 |
+ context='error message', |
|
419 |
+ flags='python-brace-format', |
|
420 |
+ ) |
|
421 |
+ PARAMS_NO_SERVICE = _prepare_translatable( |
|
422 |
+ comments=r""" |
|
423 |
+ TRANSLATORS: The param is a long-form command-line option name, |
|
424 |
+ and "SERVICE" is the command-line argument for the (sometimes |
|
425 |
+ optional) service name. |
|
426 |
+ """, |
|
427 |
+ msg='{param!s} does not take a SERVICE argument.', |
|
428 |
+ context='error message', |
|
429 |
+ flags='python-brace-format', |
|
430 |
+ ) |
|
431 |
+ SERVICE_REQUIRED = _prepare_translatable( |
|
432 |
+ 'Generating a passphrase requires a SERVICE.', |
|
433 |
+ comments='', |
|
434 |
+ context='error message', |
|
435 |
+ ) |
|
436 |
+ SSH_KEY_NOT_LOADED = _prepare_translatable( |
|
437 |
+ 'The requested SSH key is not loaded into the agent.', |
|
438 |
+ comments='', |
|
439 |
+ context='error message', |
|
440 |
+ ) |
|
441 |
+ USER_ABORTED_EDIT = _prepare_translatable( |
|
442 |
+ 'Not saving any new notes: the user aborted the request.', |
|
443 |
+ comments='', |
|
444 |
+ context='error message', |
|
445 |
+ ) |
|
446 |
+ USER_ABORTED_PASSPHRASE = _prepare_translatable( |
|
447 |
+ 'No passphrase was given; the user aborted the request.', |
|
448 |
+ comments='', |
|
449 |
+ context='error message', |
|
450 |
+ ) |
|
451 |
+ USER_ABORTED_SSH_KEY_SELECTION = _prepare_translatable( |
|
452 |
+ 'No SSH key was selected; the user aborted the request.', |
|
453 |
+ comments='', |
|
454 |
+ context='error message', |
|
455 |
+ ) |
|
456 |
+ USER_CONFIG_INVALID = _prepare_translatable( |
|
457 |
+ comments=r""" |
|
458 |
+ TRANSLATORS: The error message is usually supplied by the |
|
459 |
+ operating system, e.g. ENOENT/"No such file or directory". |
|
460 |
+ """, |
|
461 |
+ msg='The user configuration file is invalid. ' |
|
462 |
+ '{error!s}: {filename!r}.', |
|
463 |
+ context='error message', |
|
464 |
+ flags='python-brace-format', |
|
465 |
+ ) |
|
0 | 466 |