b225fb3b9cf104a042d810a2b82cd484cc7d5f33
Marco Ricci Change the author e-mail ad...

Marco Ricci authored 4 months ago

1) # SPDX-FileCopyrightText: 2024 Marco Ricci <software@the13thletter.info>
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2) #
3) # SPDX-License-Identifier: MIT
4) 
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

5) # ruff: noqa: TRY400
6) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

7) """Command-line interface for derivepassphrase."""
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

8) 
9) from __future__ import annotations
10) 
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

11) import base64
12) import collections
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

13) import copy
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

14) import enum
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

15) import functools
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

16) import importlib
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

17) import inspect
18) import json
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

19) import logging
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

20) import os
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

21) import shlex
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

22) import sys
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

23) import unicodedata
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

24) import warnings
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

25) from typing import (
26)     TYPE_CHECKING,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

27)     Callable,
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

28)     Literal,
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

29)     NoReturn,
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

30)     TextIO,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

31)     TypeVar,
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

32)     cast,
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

33) )
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

34) 
35) import click
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

36) from typing_extensions import (
37)     Any,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

38)     ParamSpec,
39)     Self,
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

40)     assert_never,
41) )
42) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

43) import derivepassphrase as dpp
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

44) from derivepassphrase import _cli_msg as _msg
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

45) from derivepassphrase import _types, exporter, ssh_agent, vault
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

46) 
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

47) if sys.version_info >= (3, 11):
48)     import tomllib
49) else:
50)     import tomli as tomllib
51) 
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

52) if TYPE_CHECKING:
53)     import pathlib
Marco Ricci Support one-off SSH agent c...

Marco Ricci authored 1 month ago

54)     import socket
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

55)     import types
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

56)     from collections.abc import (
57)         Iterator,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

58)         MutableSequence,
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

59)         Sequence,
60)     )
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

61) 
62) __author__ = dpp.__author__
63) __version__ = dpp.__version__
64) 
65) __all__ = ('derivepassphrase',)
66) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

67) PROG_NAME = _msg.PROG_NAME
Marco Ricci Make suitable SSH key listi...

Marco Ricci authored 1 month ago

68) KEY_DISPLAY_LENGTH = 50
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

69) 
70) # Error messages
71) _INVALID_VAULT_CONFIG = 'Invalid vault config'
72) _AGENT_COMMUNICATION_ERROR = 'Error communicating with the SSH agent'
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

73) _NO_SUITABLE_KEYS = 'No suitable SSH keys were found'
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

74) _EMPTY_SELECTION = 'Empty selection'
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

75) _NOT_AN_INTEGER = 'not an integer'
76) _NOT_A_NONNEGATIVE_INTEGER = 'not a non-negative integer'
77) _NOT_A_POSITIVE_INTEGER = 'not a positive integer'
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

78) 
79) 
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

80) # Logging
81) # =======
82) 
83) 
84) class ClickEchoStderrHandler(logging.Handler):
85)     """A [`logging.Handler`][] for `click` applications.
86) 
87)     Outputs log messages to [`sys.stderr`][] via [`click.echo`][].
88) 
89)     """
90) 
91)     def emit(self, record: logging.LogRecord) -> None:
92)         """Emit a log record.
93) 
94)         Format the log record, then emit it via [`click.echo`][] to
95)         [`sys.stderr`][].
96) 
97)         """
98)         click.echo(self.format(record), err=True)
99) 
100) 
101) class CLIofPackageFormatter(logging.Formatter):
102)     """A [`logging.LogRecord`][] formatter for the CLI of a Python package.
103) 
104)     Assuming a package `PKG` and loggers within the same hierarchy
105)     `PKG`, format all log records from that hierarchy for proper user
106)     feedback on the console.  Intended for use with [`click`][CLICK] and
107)     when `PKG` provides a command-line tool `PKG` and when logs from
108)     that package should show up as output of the command-line tool.
109) 
110)     Essentially, this prepends certain short strings to the log message
111)     lines to make them readable as standard error output.
112) 
113)     Because this log output is intended to be displayed on standard
114)     error as high-level diagnostic output, you are strongly discouraged
115)     from changing the output format to include more tokens besides the
116)     log message.  Use a dedicated log file handler instead, without this
117)     formatter.
118) 
119)     [CLICK]: https://pypi.org/projects/click/
120) 
121)     """
122) 
123)     def __init__(
124)         self,
125)         *,
126)         prog_name: str = PROG_NAME,
127)         package_name: str | None = None,
128)     ) -> None:
129)         self.prog_name = prog_name
130)         self.package_name = (
131)             package_name
132)             if package_name is not None
133)             else prog_name.lower().replace(' ', '_').replace('-', '_')
134)         )
135) 
136)     def format(self, record: logging.LogRecord) -> str:
137)         """Format a log record suitably for standard error console output.
138) 
139)         Prepend the formatted string `"PROG_NAME: LABEL"` to each line
140)         of the message, where `PROG_NAME` is the program name, and
141)         `LABEL` depends on the record's level and on the logger name as
142)         follows:
143) 
144)           * For records at level [`logging.DEBUG`][], `LABEL` is
145)             `"Debug: "`.
146)           * For records at level [`logging.INFO`][], `LABEL` is the
147)             empty string.
148)           * For records at level [`logging.WARNING`][], `LABEL` is
149)             `"Deprecation warning: "` if the logger is named
150)             `PKG.deprecation` (where `PKG` is the package name), else
151)             `"Warning: "`.
152)           * For records at level [`logging.ERROR`][] and
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

153)             [`logging.CRITICAL`][] `"Error: "`, `LABEL` is the empty
154)             string.
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

155) 
156)         The level indication strings at level `WARNING` or above are
157)         highlighted.  Use [`click.echo`][] to output them and remove
158)         color output if necessary.
159) 
160)         Args:
161)             record: A log record.
162) 
163)         Returns:
164)             A formatted log record.
165) 
166)         Raises:
167)             AssertionError:
168)                 The log level is not supported.
169) 
170)         """
171)         preliminary_result = record.getMessage()
172)         prefix = f'{self.prog_name}: '
173)         if record.levelname == 'DEBUG':  # pragma: no cover
174)             level_indicator = 'Debug: '
175)         elif record.levelname == 'INFO':
176)             level_indicator = ''
177)         elif record.levelname == 'WARNING':
178)             level_indicator = (
179)                 f'{click.style("Deprecation warning", bold=True)}: '
180)                 if record.name.endswith('.deprecation')
181)                 else f'{click.style("Warning", bold=True)}: '
182)             )
183)         elif record.levelname in {'ERROR', 'CRITICAL'}:
184)             level_indicator = ''
185)         else:  # pragma: no cover
186)             msg = f'Unsupported logging level: {record.levelname}'
187)             raise AssertionError(msg)
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

188)         parts = [
189)             ''.join(
190)                 prefix + level_indicator + line
191)                 for line in preliminary_result.splitlines(True)  # noqa: FBT003
192)             )
193)         ]
194)         if record.exc_info:
195)             parts.append(self.formatException(record.exc_info) + '\n')
196)         return ''.join(parts)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

197) 
198) 
199) class StandardCLILogging:
200)     """Set up CLI logging handlers upon instantiation."""
201) 
202)     prog_name = PROG_NAME
203)     package_name = PROG_NAME.lower().replace(' ', '_').replace('-', '_')
204)     cli_formatter = CLIofPackageFormatter(
205)         prog_name=prog_name, package_name=package_name
206)     )
207)     cli_handler = ClickEchoStderrHandler()
208)     cli_handler.addFilter(logging.Filter(name=package_name))
209)     cli_handler.setFormatter(cli_formatter)
210)     cli_handler.setLevel(logging.WARNING)
211)     warnings_handler = ClickEchoStderrHandler()
212)     warnings_handler.addFilter(logging.Filter(name='py.warnings'))
213)     warnings_handler.setFormatter(cli_formatter)
214)     warnings_handler.setLevel(logging.WARNING)
215) 
216)     @classmethod
217)     def ensure_standard_logging(cls) -> StandardLoggingContextManager:
218)         """Return a context manager to ensure standard logging is set up."""
219)         return StandardLoggingContextManager(
220)             handler=cls.cli_handler,
221)             root_logger=cls.package_name,
222)         )
223) 
224)     @classmethod
225)     def ensure_standard_warnings_logging(
226)         cls,
227)     ) -> StandardWarningsLoggingContextManager:
228)         """Return a context manager to ensure warnings logging is set up."""
229)         return StandardWarningsLoggingContextManager(
230)             handler=cls.warnings_handler,
231)         )
232) 
233) 
234) class StandardLoggingContextManager:
235)     """A reentrant context manager setting up standard CLI logging.
236) 
237)     Ensures that the given handler (defaulting to the CLI logging
238)     handler) is added to the named logger (defaulting to the root
239)     logger), and if it had to be added, then that it will be removed
240)     upon exiting the context.
241) 
242)     Reentrant, but not thread safe, because it temporarily modifies
243)     global state.
244) 
245)     """
246) 
247)     def __init__(
248)         self,
249)         handler: logging.Handler,
250)         root_logger: str | None = None,
251)     ) -> None:
252)         self.handler = handler
253)         self.root_logger_name = root_logger
254)         self.base_logger = logging.getLogger(self.root_logger_name)
255)         self.action_required: MutableSequence[bool] = collections.deque()
256) 
257)     def __enter__(self) -> Self:
258)         self.action_required.append(
259)             self.handler not in self.base_logger.handlers
260)         )
261)         if self.action_required[-1]:
262)             self.base_logger.addHandler(self.handler)
263)         return self
264) 
265)     def __exit__(
266)         self,
267)         exc_type: type[BaseException] | None,
268)         exc_value: BaseException | None,
269)         exc_tb: types.TracebackType | None,
270)     ) -> Literal[False]:
271)         if self.action_required[-1]:
272)             self.base_logger.removeHandler(self.handler)
273)         self.action_required.pop()
274)         return False
275) 
276) 
277) class StandardWarningsLoggingContextManager(StandardLoggingContextManager):
278)     """A reentrant context manager setting up standard warnings logging.
279) 
280)     Ensures that warnings are being diverted to the logging system, and
281)     that the given handler (defaulting to the CLI logging handler) is
282)     added to the warnings logger. If the handler had to be added, then
283)     it will be removed upon exiting the context.
284) 
285)     Reentrant, but not thread safe, because it temporarily modifies
286)     global state.
287) 
288)     """
289) 
290)     def __init__(
291)         self,
292)         handler: logging.Handler,
293)     ) -> None:
294)         super().__init__(handler=handler, root_logger='py.warnings')
295)         self.stack: MutableSequence[
296)             tuple[
297)                 Callable[
298)                     [
299)                         type[BaseException] | None,
300)                         BaseException | None,
301)                         types.TracebackType | None,
302)                     ],
303)                     None,
304)                 ],
305)                 Callable[
306)                     [
307)                         str | Warning,
308)                         type[Warning],
309)                         str,
310)                         int,
311)                         TextIO | None,
312)                         str | None,
313)                     ],
314)                     None,
315)                 ],
316)             ]
317)         ] = collections.deque()
318) 
319)     def __enter__(self) -> Self:
320)         def showwarning(  # noqa: PLR0913,PLR0917
321)             message: str | Warning,
322)             category: type[Warning],
323)             filename: str,
324)             lineno: int,
325)             file: TextIO | None = None,
326)             line: str | None = None,
327)         ) -> None:
328)             if file is not None:  # pragma: no cover
329)                 self.stack[0][1](
330)                     message, category, filename, lineno, file, line
331)                 )
332)             else:
333)                 logging.getLogger('py.warnings').warning(
334)                     str(
335)                         warnings.formatwarning(
336)                             message, category, filename, lineno, line
337)                         )
338)                     )
339)                 )
340) 
341)         ctx = warnings.catch_warnings()
342)         exit_func = ctx.__exit__
343)         ctx.__enter__()
344)         self.stack.append((exit_func, warnings.showwarning))
345)         warnings.showwarning = showwarning
346)         return super().__enter__()
347) 
348)     def __exit__(
349)         self,
350)         exc_type: type[BaseException] | None,
351)         exc_value: BaseException | None,
352)         exc_tb: types.TracebackType | None,
353)     ) -> Literal[False]:
354)         ret = super().__exit__(exc_type, exc_value, exc_tb)
355)         val = self.stack.pop()[0](exc_type, exc_value, exc_tb)
356)         assert not val
357)         return ret
358) 
359) 
360) P = ParamSpec('P')
361) R = TypeVar('R')
362) 
363) 
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

364) def adjust_logging_level(
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

365)     ctx: click.Context,
366)     /,
367)     param: click.Parameter | None = None,
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

368)     value: int | None = None,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

369) ) -> None:
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

370)     """Change the logs that are emitted to standard error.
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

371) 
372)     This modifies the [`StandardCLILogging`][] settings such that log
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

373)     records at the respective level are emitted, based on the `param`
374)     and the `value`.
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

375) 
376)     """
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

377)     # Note: If multiple options use this callback, then we will be
378)     # called multiple times.  Ensure the runs are idempotent.
379)     if param is None or value is None or ctx.resilient_parsing:
380)         return
381)     StandardCLILogging.cli_handler.setLevel(value)
382)     logging.getLogger(StandardCLILogging.package_name).setLevel(value)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

383) 
384) 
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

385) # Option parsing and grouping
386) # ===========================
387) 
388) 
389) class OptionGroupOption(click.Option):
390)     """A [`click.Option`][] with an associated group name and group epilog.
391) 
392)     Used by [`CommandWithHelpGroups`][] to print help sections.  Each
393)     subclass contains its own group name and epilog.
394) 
395)     Attributes:
396)         option_group_name:
397)             The name of the option group.  Used as a heading on the help
398)             text for options in this section.
399)         epilog:
400)             An epilog to print after listing the options in this
401)             section.
402) 
403)     """
404) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

405)     option_group_name: object = ''
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

406)     """"""
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

407)     epilog: object = ''
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

408)     """"""
409) 
410)     def __init__(self, *args: Any, **kwargs: Any) -> None:  # noqa: ANN401
411)         if self.__class__ == __class__:  # type: ignore[name-defined]
412)             raise NotImplementedError
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

413)         # Though click 8.1 mostly defers help text processing until the
414)         # `BaseCommand.format_*` methods are called, the Option
415)         # constructor still preprocesses the help text, and asserts that
416)         # the help text is a string.  Work around this by removing the
417)         # help text from the constructor arguments and re-adding it,
418)         # unprocessed, after constructor finishes.
419)         unset = object()
420)         help = kwargs.pop('help', unset)  # noqa: A001
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

421)         super().__init__(*args, **kwargs)
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

422)         if help is not unset:  # pragma: no branch
423)             self.help = help
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

424) 
425) 
426) class CommandWithHelpGroups(click.Command):
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

427)     """A [`click.Command`][] with support for some help text customizations.
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

428) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

429)     Supports help/option groups, group epilogs, and help text objects
430)     (objects that stringify to help texts).  The latter is primarily
431)     used to implement translations.
432) 
433)     Inspired by [a comment on `pallets/click#373`][CLICK_ISSUE] for
434)     help/option group support, and further modified to include group
435)     epilogs and help text objects.
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

436) 
437)     [CLICK_ISSUE]: https://github.com/pallets/click/issues/373#issuecomment-515293746
438) 
439)     """
440) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

441)     @staticmethod
442)     def _text(text: object, /) -> str:
443)         if isinstance(text, (list, tuple)):
444)             return '\n\n'.join(str(x) for x in text)
445)         return str(text)
446) 
447)     def collect_usage_pieces(self, ctx: click.Context) -> list[str]:
448)         """Return the pieces for the usage string.
449) 
450)         Based on code from click 8.1.  Subject to the following license
451)         (3-clause BSD license):
452) 
453)             Copyright 2024 Pallets
454) 
455)             Redistribution and use in source and binary forms, with or
456)             without modification, are permitted provided that the
457)             following conditions are met:
458) 
459)              1. Redistributions of source code must retain the above
460)                 copyright notice, this list of conditions and the
461)                 following disclaimer.
462) 
463)              2. Redistributions in binary form must reproduce the above
464)                 copyright notice, this list of conditions and the
465)                 following disclaimer in the documentation and/or other
466)                 materials provided with the distribution.
467) 
468)              3. Neither the name of the copyright holder nor the names
469)                 of its contributors may be used to endorse or promote
470)                 products derived from this software without specific
471)                 prior written permission.
472) 
473)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
474)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
475)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
476)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
477)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
478)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
479)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
480)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
481)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
482)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
483)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
484)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
485)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
486) 
487)         Modifications are marked with respective comments.  They too are
488)         released under the same license above.  The original code did
489)         not contain any "noqa" or "pragma" comments.
490) 
491)         Args:
492)             ctx:
493)                 The click context.
494) 
495)         """
496)         rv = [str(self.options_metavar)] if self.options_metavar else []
497)         for param in self.get_params(ctx):
498)             rv.extend(str(x) for x in param.get_usage_pieces(ctx))
499)         return rv
500) 
501)     def get_short_help_str(
502)         self,
503)         limit: int = 45,
504)     ) -> str:
505)         """Return the short help string for a command.
506) 
507)         If only a long help string is given, shorten it.
508) 
509)         Based on code from click 8.1.  Subject to the following license
510)         (3-clause BSD license):
511) 
512)             Copyright 2024 Pallets
513) 
514)             Redistribution and use in source and binary forms, with or
515)             without modification, are permitted provided that the
516)             following conditions are met:
517) 
518)              1. Redistributions of source code must retain the above
519)                 copyright notice, this list of conditions and the
520)                 following disclaimer.
521) 
522)              2. Redistributions in binary form must reproduce the above
523)                 copyright notice, this list of conditions and the
524)                 following disclaimer in the documentation and/or other
525)                 materials provided with the distribution.
526) 
527)              3. Neither the name of the copyright holder nor the names
528)                 of its contributors may be used to endorse or promote
529)                 products derived from this software without specific
530)                 prior written permission.
531) 
532)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
533)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
534)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
535)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
536)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
537)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
538)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
539)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
540)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
541)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
542)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
543)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
544)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
545) 
546)         Modifications are marked with respective comments.  They too are
547)         released under the same license above.  The original code did
548)         not contain any "noqa" or "pragma" comments.
549) 
550)         Args:
551)             limit:
552)                 The maximum width of the short help string.
553) 
554)         """
555)         # Modification against click 8.1: Call `_text()` on `self.help`
556)         # to allow help texts to be general objects, not just strings.
557)         # Used to implement translatable strings, as objects that
558)         # stringify to the translation.
559)         if self.short_help:  # pragma: no cover
560)             text = inspect.cleandoc(self._text(self.short_help))
561)         elif self.help:
562)             text = click.utils.make_default_short_help(
563)                 self._text(self.help), limit
564)             )
565)         else:  # pragma: no cover
566)             text = ''
567)         if self.deprecated:  # pragma: no cover
568)             # Modification against click 8.1: The translated string is
569)             # looked up in the derivepassphrase message domain, not the
570)             # gettext default domain.
571)             text = str(
572)                 _msg.TranslatedString(_msg.Label.DEPRECATED_COMMAND_LABEL)
573)             ).format(text=text)
574)         return text.strip()
575) 
576)     def format_help_text(
577)         self,
578)         ctx: click.Context,
579)         formatter: click.HelpFormatter,
580)     ) -> None:
581)         """Format the help text prologue, if any.
582) 
583)         Based on code from click 8.1.  Subject to the following license
584)         (3-clause BSD license):
585) 
586)             Copyright 2024 Pallets
587) 
588)             Redistribution and use in source and binary forms, with or
589)             without modification, are permitted provided that the
590)             following conditions are met:
591) 
592)              1. Redistributions of source code must retain the above
593)                 copyright notice, this list of conditions and the
594)                 following disclaimer.
595) 
596)              2. Redistributions in binary form must reproduce the above
597)                 copyright notice, this list of conditions and the
598)                 following disclaimer in the documentation and/or other
599)                 materials provided with the distribution.
600) 
601)              3. Neither the name of the copyright holder nor the names
602)                 of its contributors may be used to endorse or promote
603)                 products derived from this software without specific
604)                 prior written permission.
605) 
606)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
607)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
608)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
609)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
610)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
611)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
612)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
613)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
614)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
615)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
616)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
617)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
618)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
619) 
620)         Modifications are marked with respective comments.  They too are
621)         released under the same license above.  The original code did
622)         not contain any "noqa" or "pragma" comments.
623) 
624)         Args:
625)             ctx:
626)                 The click context.
627)             formatter:
628)                 The formatter for the `--help` listing.
629) 
630)         """
631)         del ctx
632)         # Modification against click 8.1: Call `_text()` on `self.help`
633)         # to allow help texts to be general objects, not just strings.
634)         # Used to implement translatable strings, as objects that
635)         # stringify to the translation.
636)         text = (
637)             inspect.cleandoc(self._text(self.help).partition('\f')[0])
638)             if self.help is not None
639)             else ''
640)         )
641)         if self.deprecated:  # pragma: no cover
642)             # Modification against click 8.1: The translated string is
643)             # looked up in the derivepassphrase message domain, not the
644)             # gettext default domain.
645)             text = str(
646)                 _msg.TranslatedString(_msg.Label.DEPRECATED_COMMAND_LABEL)
647)             ).format(text=text)
648)         if text:  # pragma: no branch
649)             formatter.write_paragraph()
650)             with formatter.indentation():
651)                 formatter.write_text(text)
652) 
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

653)     def format_options(
654)         self,
655)         ctx: click.Context,
656)         formatter: click.HelpFormatter,
657)     ) -> None:
658)         r"""Format options on the help listing, grouped into sections.
659) 
660)         This is a callback for [`click.Command.get_help`][] that
661)         implements the `--help` listing, by calling appropriate methods
662)         of the `formatter`.  We list all options (like the base
663)         implementation), but grouped into sections according to the
664)         concrete [`click.Option`][] subclass being used.  If the option
665)         is an instance of some subclass of [`OptionGroupOption`][], then
666)         the section heading and the epilog are taken from the
667)         [`option_group_name`] [OptionGroupOption.option_group_name] and
668)         [`epilog`] [OptionGroupOption.epilog] attributes; otherwise, the
669)         section heading is "Options" (or "Other options" if there are
670)         other option groups) and the epilog is empty.
671) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

672)         We unconditionally call [`format_commands`][], and rely on it to
673)         act as a no-op if we aren't actually a [`click.MultiCommand`][].
674) 
675)         Based on code from click 8.1.  Subject to the following license
676)         (3-clause BSD license):
677) 
678)             Copyright 2024 Pallets
679) 
680)             Redistribution and use in source and binary forms, with or
681)             without modification, are permitted provided that the
682)             following conditions are met:
683) 
684)              1. Redistributions of source code must retain the above
685)                 copyright notice, this list of conditions and the
686)                 following disclaimer.
687) 
688)              2. Redistributions in binary form must reproduce the above
689)                 copyright notice, this list of conditions and the
690)                 following disclaimer in the documentation and/or other
691)                 materials provided with the distribution.
692) 
693)              3. Neither the name of the copyright holder nor the names
694)                 of its contributors may be used to endorse or promote
695)                 products derived from this software without specific
696)                 prior written permission.
697) 
698)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
699)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
700)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
701)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
702)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
703)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
704)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
705)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
706)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
707)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
708)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
709)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
710)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
711) 
712)         Modifications are released under the same license above.
713) 
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

714)         Args:
715)             ctx:
716)                 The click context.
717)             formatter:
718)                 The formatter for the `--help` listing.
719) 
720)         """
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

721)         default_group_name = ''
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

722)         help_records: dict[str, list[tuple[str, str]]] = {}
723)         epilogs: dict[str, str] = {}
724)         params = self.params[:]
725)         if (  # pragma: no branch
726)             (help_opt := self.get_help_option(ctx)) is not None
727)             and help_opt not in params
728)         ):
729)             params.append(help_opt)
730)         for param in params:
731)             rec = param.get_help_record(ctx)
732)             if rec is not None:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

733)                 rec = (rec[0], self._text(rec[1]))
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

734)                 if isinstance(param, OptionGroupOption):
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

735)                     group_name = self._text(param.option_group_name)
736)                     epilogs.setdefault(group_name, self._text(param.epilog))
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

737)                 else:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

738)                     group_name = default_group_name
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

739)                 help_records.setdefault(group_name, []).append(rec)
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

740)         if default_group_name in help_records:  # pragma: no branch
741)             default_group = help_records.pop(default_group_name)
742)             default_group_label = (
743)                 _msg.Label.OTHER_OPTIONS_LABEL
744)                 if len(default_group) > 1
745)                 else _msg.Label.OPTIONS_LABEL
746)             )
747)             default_group_name = self._text(
748)                 _msg.TranslatedString(default_group_label)
749)             )
750)             help_records[default_group_name] = default_group
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

751)         for group_name, records in help_records.items():
752)             with formatter.section(group_name):
753)                 formatter.write_dl(records)
754)             epilog = inspect.cleandoc(epilogs.get(group_name, ''))
755)             if epilog:
756)                 formatter.write_paragraph()
757)                 with formatter.indentation():
758)                     formatter.write_text(epilog)
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

759)         self.format_commands(ctx, formatter)
760) 
761)     def format_commands(
762)         self,
763)         ctx: click.Context,
764)         formatter: click.HelpFormatter,
765)     ) -> None:
766)         """Format the subcommands, if any.
767) 
768)         If called on a command object that isn't derived from
769)         [`click.MultiCommand`][], then do nothing.
770) 
771)         Based on code from click 8.1.  Subject to the following license
772)         (3-clause BSD license):
773) 
774)             Copyright 2024 Pallets
775) 
776)             Redistribution and use in source and binary forms, with or
777)             without modification, are permitted provided that the
778)             following conditions are met:
779) 
780)              1. Redistributions of source code must retain the above
781)                 copyright notice, this list of conditions and the
782)                 following disclaimer.
783) 
784)              2. Redistributions in binary form must reproduce the above
785)                 copyright notice, this list of conditions and the
786)                 following disclaimer in the documentation and/or other
787)                 materials provided with the distribution.
788) 
789)              3. Neither the name of the copyright holder nor the names
790)                 of its contributors may be used to endorse or promote
791)                 products derived from this software without specific
792)                 prior written permission.
793) 
794)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
795)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
796)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
797)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
798)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
799)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
800)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
801)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
802)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
803)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
804)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
805)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
806)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
807) 
808)         Modifications are marked with respective comments.  They too are
809)         released under the same license above.  The original code did
810)         not contain any "noqa" or "pragma" comments.
811) 
812)         Args:
813)             ctx:
814)                 The click context.
815)             formatter:
816)                 The formatter for the `--help` listing.
817) 
818)         """
819)         if not isinstance(self, click.MultiCommand):
820)             return
821)         commands: list[tuple[str, click.Command]] = []
822)         for subcommand in self.list_commands(ctx):
823)             cmd = self.get_command(ctx, subcommand)
824)             if cmd is None or cmd.hidden:  # pragma: no cover
825)                 continue
826)             commands.append((subcommand, cmd))
827)         if commands:  # pragma: no branch
828)             longest_command = max((cmd[0] for cmd in commands), key=len)
829)             limit = formatter.width - 6 - len(longest_command)
830)             rows: list[tuple[str, str]] = []
831)             for subcommand, cmd in commands:
832)                 help_str = self._text(cmd.get_short_help_str(limit) or '')
833)                 rows.append((subcommand, help_str))
834)             if rows:  # pragma: no branch
835)                 commands_label = self._text(
836)                     _msg.TranslatedString(_msg.Label.COMMANDS_LABEL)
837)                 )
838)                 with formatter.section(commands_label):
839)                     formatter.write_dl(rows)
840) 
841)     def format_epilog(
842)         self,
843)         ctx: click.Context,
844)         formatter: click.HelpFormatter,
845)     ) -> None:
846)         """Format the epilog, if any.
847) 
848)         Based on code from click 8.1.  Subject to the following license
849)         (3-clause BSD license):
850) 
851)             Copyright 2024 Pallets
852) 
853)             Redistribution and use in source and binary forms, with or
854)             without modification, are permitted provided that the
855)             following conditions are met:
856) 
857)              1. Redistributions of source code must retain the above
858)                 copyright notice, this list of conditions and the
859)                 following disclaimer.
860) 
861)              2. Redistributions in binary form must reproduce the above
862)                 copyright notice, this list of conditions and the
863)                 following disclaimer in the documentation and/or other
864)                 materials provided with the distribution.
865) 
866)              3. Neither the name of the copyright holder nor the names
867)                 of its contributors may be used to endorse or promote
868)                 products derived from this software without specific
869)                 prior written permission.
870) 
871)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
872)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
873)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
874)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
875)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
876)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
877)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
878)             NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
879)             LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
880)             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
881)             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
882)             OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
883)             SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
884) 
885)         Modifications are marked with respective comments.  They too are
886)         released under the same license above.
887) 
888)         Args:
889)             ctx:
890)                 The click context.
891)             formatter:
892)                 The formatter for the `--help` listing.
893) 
894)         """
895)         del ctx
896)         if self.epilog:  # pragma: no branch
897)             # Modification against click 8.1: Call `str()` on
898)             # `self.epilog` to allow help texts to be general objects,
899)             # not just strings.  Used to implement translatable strings,
900)             # as objects that stringify to the translation.
901)             epilog = inspect.cleandoc(self._text(self.epilog))
902)             formatter.write_paragraph()
903)             with formatter.indentation():
904)                 formatter.write_text(epilog)
Marco Ricci Shift option parsing and gr...

Marco Ricci authored 1 month ago

905) 
906) 
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

907) class LoggingOption(OptionGroupOption):
908)     """Logging options for the CLI."""
909) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

910)     option_group_name = _msg.TranslatedString(_msg.Label.LOGGING_LABEL)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

911)     epilog = ''
912) 
913) 
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

914) debug_option = click.option(
915)     '--debug',
916)     'logging_level',
917)     is_flag=True,
918)     flag_value=logging.DEBUG,
919)     expose_value=False,
920)     callback=adjust_logging_level,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

921)     help=_msg.TranslatedString(_msg.Label.DEBUG_OPTION_HELP_TEXT),
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

922)     cls=LoggingOption,
923) )
924) verbose_option = click.option(
925)     '-v',
926)     '--verbose',
927)     'logging_level',
928)     is_flag=True,
929)     flag_value=logging.INFO,
930)     expose_value=False,
931)     callback=adjust_logging_level,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

932)     help=_msg.TranslatedString(_msg.Label.VERBOSE_OPTION_HELP_TEXT),
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

933)     cls=LoggingOption,
934) )
935) quiet_option = click.option(
936)     '-q',
937)     '--quiet',
938)     'logging_level',
939)     is_flag=True,
940)     flag_value=logging.ERROR,
941)     expose_value=False,
942)     callback=adjust_logging_level,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

943)     help=_msg.TranslatedString(_msg.Label.QUIET_OPTION_HELP_TEXT),
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

944)     cls=LoggingOption,
945) )
946) 
947) 
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

948) def standard_logging_options(f: Callable[P, R]) -> Callable[P, R]:
949)     """Decorate the function with standard logging click options.
950) 
951)     Adds the three click options `-v`/`--verbose`, `-q`/`--quiet` and
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

952)     `--debug`, which calls back into the [`adjust_logging_level`][]
953)     function (with different argument values).
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

954) 
955)     Args:
956)         f: A callable to decorate.
957) 
958)     Returns:
959)         The decorated callable.
960) 
961)     """
Marco Ricci Fix usage of `--debug`, `--...

Marco Ricci authored 3 weeks ago

962)     return debug_option(verbose_option(quiet_option(f)))
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

963) 
964) 
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

965) # Top-level
966) # =========
967) 
968) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

969) class _DefaultToVaultGroup(CommandWithHelpGroups, click.Group):
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

970)     """A helper class to implement the default-to-"vault"-subcommand behavior.
971) 
972)     Modifies internal [`click.MultiCommand`][] methods, and thus is both
973)     an implementation detail and a kludge.
974) 
975)     """
976) 
977)     def resolve_command(
978)         self, ctx: click.Context, args: list[str]
979)     ) -> tuple[str | None, click.Command | None, list[str]]:
980)         """Resolve a command, but default to "vault" instead of erroring out.
981) 
982)         Based on code from click 8.1, which appears to be essentially
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

983)         untouched since at least click 3.2.  Subject to the following
984)         license (3-clause BSD license):
985) 
986)             Copyright 2024 Pallets
987) 
988)             Redistribution and use in source and binary forms, with or
989)             without modification, are permitted provided that the following
990)             conditions are met:
991) 
992)              1. Redistributions of source code must retain the above
993)                 copyright notice, this list of conditions and the following
994)                 disclaimer.
995) 
996)              2. Redistributions in binary form must reproduce the above
997)                 copyright notice, this list of conditions and the following
998)                 disclaimer in the documentation and/or other materials
999)                 provided with the distribution.
1000) 
1001)              3. Neither the name of the copyright holder nor the names of
1002)                 its contributors may be used to endorse or promote products
1003)                 derived from this software without specific prior written
1004)                 permission.
1005) 
1006)             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
1007)             CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
1008)             INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1009)             MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1010)             DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
1011)             CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1012)             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1013)             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
1014)             USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
1015)             AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1016)             LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
1017)             IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
1018)             THE POSSIBILITY OF SUCH DAMAGE.
1019) 
1020)         Modifications to this routine are marked with "modifications for
1021)         derivepassphrase".  Furthermore, all "pragma" and "noqa" comments
1022)         are also modifications for derivepassphrase.
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1023) 
1024)         """
1025)         cmd_name = click.utils.make_str(args[0])
1026) 
1027)         # Get the command
1028)         cmd = self.get_command(ctx, cmd_name)
1029) 
1030)         # If we can't find the command but there is a normalization
1031)         # function available, we try with that one.
1032)         if (  # pragma: no cover
1033)             cmd is None and ctx.token_normalize_func is not None
1034)         ):
1035)             cmd_name = ctx.token_normalize_func(cmd_name)
1036)             cmd = self.get_command(ctx, cmd_name)
1037) 
1038)         # If we don't find the command we want to show an error message
1039)         # to the user that it was not provided.  However, there is
1040)         # something else we should do: if the first argument looks like
1041)         # an option we want to kick off parsing again for arguments to
1042)         # resolve things like --help which now should go to the main
1043)         # place.
1044)         if cmd is None and not ctx.resilient_parsing:
1045)             if click.parser.split_opt(cmd_name)[0]:
1046)                 self.parse_args(ctx, ctx.args)
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1047)             ####
1048)             # BEGIN modifications for derivepassphrase
1049)             #
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1050)             # Instead of calling ctx.fail here, default to "vault", and
1051)             # issue a deprecation warning.
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1052)             deprecation = logging.getLogger(f'{PROG_NAME}.deprecation')
1053)             deprecation.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1054)                 _msg.TranslatedString(
1055)                     _msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED
1056)                 )
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1057)             )
1058)             cmd_name = 'vault'
1059)             cmd = self.get_command(ctx, cmd_name)
1060)             assert cmd is not None, 'Mandatory subcommand "vault" missing!'
1061)             args = [cmd_name, *args]
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1062)             #
1063)             # END modifications for derivepassphrase
1064)             ####
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1065)         return cmd_name if cmd else None, cmd, args[1:]  # noqa: DOC201
1066) 
1067) 
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1068) class _TopLevelCLIEntryPoint(_DefaultToVaultGroup):
1069)     """A minor variation of _DefaultToVaultGroup for the top-level command.
1070) 
1071)     When called as a function, this sets up the environment properly
1072)     before invoking the actual callbacks.  Currently, this means setting
1073)     up the logging subsystem and the delegation of Python warnings to
1074)     the logging subsystem.
1075) 
1076)     The environment setup can be bypassed by calling the `.main` method
1077)     directly.
1078) 
1079)     """
1080) 
1081)     def __call__(  # pragma: no cover
1082)         self,
1083)         *args: Any,  # noqa: ANN401
1084)         **kwargs: Any,  # noqa: ANN401
1085)     ) -> Any:  # noqa: ANN401
1086)         """"""  # noqa: D419
1087)         # Coverage testing is done with the `click.testing` module,
1088)         # which does not use the `__call__` shortcut.  So it is normal
1089)         # that this function is never called, and thus should be
1090)         # excluded from coverage.
1091)         with (
1092)             StandardCLILogging.ensure_standard_logging(),
1093)             StandardCLILogging.ensure_standard_warnings_logging(),
1094)         ):
1095)             return self.main(*args, **kwargs)
1096) 
1097) 
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1098) @click.group(
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1099)     context_settings={
1100)         'help_option_names': ['-h', '--help'],
1101)         'ignore_unknown_options': True,
1102)         'allow_interspersed_args': False,
1103)     },
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1104)     epilog=_msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EPILOG_01),
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1105)     invoke_without_command=True,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1106)     cls=_TopLevelCLIEntryPoint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1107)     help=(
1108)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_01),
1109)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_02),
1110)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_03),
1111)     ),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1112) )
1113) @click.version_option(version=dpp.__version__, prog_name=PROG_NAME)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1114) @standard_logging_options
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1115) @click.pass_context
1116) def derivepassphrase(ctx: click.Context, /) -> None:
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1117)     """Derive a strong passphrase, deterministically, from a master secret.
1118) 
1119)     This is a [`click`][CLICK]-powered command-line interface function,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1120)     and not intended for programmatic use.  See the derivepassphrase(1)
1121)     manpage for full documentation of the interface.  (See also
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1122)     [`click.testing.CliRunner`][] for controlled, programmatic
1123)     invocation.)
1124) 
Marco Ricci Update all URLs to stable a...

Marco Ricci authored 3 months ago

1125)     [CLICK]: https://pypi.org/package/click/
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1126) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1127)     """
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1128)     deprecation = logging.getLogger(f'{PROG_NAME}.deprecation')
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1129)     if ctx.invoked_subcommand is None:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1130)         deprecation.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1131)             _msg.TranslatedString(_msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1132)         )
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1133)         # See definition of click.Group.invoke, non-chained case.
1134)         with ctx:
1135)             sub_ctx = derivepassphrase_vault.make_context(
1136)                 'vault', ctx.args, parent=ctx
1137)             )
1138)             with sub_ctx:
1139)                 return derivepassphrase_vault.invoke(sub_ctx)
1140)     return None
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1141) 
1142) 
1143) # Exporter
1144) # ========
1145) 
1146) 
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1147) @derivepassphrase.group(
1148)     'export',
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1149)     context_settings={
1150)         'help_option_names': ['-h', '--help'],
1151)         'ignore_unknown_options': True,
1152)         'allow_interspersed_args': False,
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1153)     },
1154)     invoke_without_command=True,
1155)     cls=_DefaultToVaultGroup,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1156)     help=(
1157)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_01),
1158)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_02),
1159)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_03),
1160)     ),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1161) )
1162) @click.version_option(version=dpp.__version__, prog_name=PROG_NAME)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1163) @standard_logging_options
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1164) @click.pass_context
1165) def derivepassphrase_export(ctx: click.Context, /) -> None:
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1166)     """Export a foreign configuration to standard output.
1167) 
1168)     This is a [`click`][CLICK]-powered command-line interface function,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1169)     and not intended for programmatic use.  See the
1170)     derivepassphrase-export(1) manpage for full documentation of the
1171)     interface.  (See also [`click.testing.CliRunner`][] for controlled,
1172)     programmatic invocation.)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1173) 
Marco Ricci Update all URLs to stable a...

Marco Ricci authored 3 months ago

1174)     [CLICK]: https://pypi.org/package/click/
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1175) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1176)     """
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1177)     deprecation = logging.getLogger(f'{PROG_NAME}.deprecation')
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1178)     if ctx.invoked_subcommand is None:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1179)         deprecation.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1180)             _msg.TranslatedString(_msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1181)         )
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1182)         # See definition of click.Group.invoke, non-chained case.
1183)         with ctx:
1184)             sub_ctx = derivepassphrase_export_vault.make_context(
1185)                 'vault', ctx.args, parent=ctx
1186)             )
1187)             # Constructing the subcontext above will usually already
1188)             # lead to a click.UsageError, so this block typically won't
1189)             # actually be called.
1190)             with sub_ctx:  # pragma: no cover
1191)                 return derivepassphrase_export_vault.invoke(sub_ctx)
1192)     return None
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1193) 
1194) 
1195) def _load_data(
1196)     fmt: Literal['v0.2', 'v0.3', 'storeroom'],
1197)     path: str | bytes | os.PathLike[str],
1198)     key: bytes,
1199) ) -> Any:  # noqa: ANN401
1200)     contents: bytes
1201)     module: types.ModuleType
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

1202)     # Use match/case here once Python 3.9 becomes unsupported.
1203)     if fmt == 'v0.2':
1204)         module = importlib.import_module(
1205)             'derivepassphrase.exporter.vault_native'
1206)         )
1207)         if module.STUBBED:
1208)             raise ModuleNotFoundError
1209)         with open(path, 'rb') as infile:
1210)             contents = base64.standard_b64decode(infile.read())
1211)         return module.export_vault_native_data(
1212)             contents, key, try_formats=['v0.2']
1213)         )
1214)     elif fmt == 'v0.3':  # noqa: RET505
1215)         module = importlib.import_module(
1216)             'derivepassphrase.exporter.vault_native'
1217)         )
1218)         if module.STUBBED:
1219)             raise ModuleNotFoundError
1220)         with open(path, 'rb') as infile:
1221)             contents = base64.standard_b64decode(infile.read())
1222)         return module.export_vault_native_data(
1223)             contents, key, try_formats=['v0.3']
1224)         )
1225)     elif fmt == 'storeroom':
1226)         module = importlib.import_module('derivepassphrase.exporter.storeroom')
1227)         if module.STUBBED:
1228)             raise ModuleNotFoundError
1229)         return module.export_storeroom_data(path, key)
1230)     else:  # pragma: no cover
1231)         assert_never(fmt)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1232) 
1233) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1234) class StandardOption(OptionGroupOption):
1235)     pass
1236) 
1237) 
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

1238) @derivepassphrase_export.command(
1239)     'vault',
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1240)     context_settings={'help_option_names': ['-h', '--help']},
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1241)     cls=CommandWithHelpGroups,
1242)     help=(
1243)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_VAULT_01),
1244)         _msg.TranslatedString(
1245)             _msg.Label.DERIVEPASSPHRASE_EXPORT_VAULT_02,
1246)             path_metavar=_msg.TranslatedString(
1247)                 _msg.Label.EXPORT_VAULT_METAVAR_PATH,
1248)             ),
1249)         ),
1250)         _msg.TranslatedString(
1251)             _msg.Label.DERIVEPASSPHRASE_EXPORT_VAULT_03,
1252)             path_metavar=_msg.TranslatedString(
1253)                 _msg.Label.EXPORT_VAULT_METAVAR_PATH,
1254)             ),
1255)         ),
1256)     ),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1257) )
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1258) @standard_logging_options
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1259) @click.option(
1260)     '-f',
1261)     '--format',
1262)     'formats',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1263)     metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_FORMAT_METAVAR_FMT),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1264)     multiple=True,
1265)     default=('v0.3', 'v0.2', 'storeroom'),
1266)     type=click.Choice(['v0.2', 'v0.3', 'storeroom']),
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1267)     help=_msg.TranslatedString(
1268)         _msg.Label.EXPORT_VAULT_FORMAT_HELP_TEXT,
1269)         defaults_hint=_msg.TranslatedString(
1270)             _msg.Label.EXPORT_VAULT_FORMAT_DEFAULTS_HELP_TEXT,
1271)         ),
1272)         metavar=_msg.TranslatedString(
1273)             _msg.Label.EXPORT_VAULT_FORMAT_METAVAR_FMT,
1274)         ),
1275)     ),
1276)     cls=StandardOption,
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1277) )
1278) @click.option(
1279)     '-k',
1280)     '--key',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1281)     metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_KEY_METAVAR_K),
1282)     help=_msg.TranslatedString(
1283)         _msg.Label.EXPORT_VAULT_KEY_HELP_TEXT,
1284)         metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_KEY_METAVAR_K),
1285)         defaults_hint=_msg.TranslatedString(
1286)             _msg.Label.EXPORT_VAULT_KEY_DEFAULTS_HELP_TEXT,
1287)         ),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1288)     ),
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1289)     cls=StandardOption,
1290) )
1291) @click.argument(
1292)     'path',
1293)     metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_METAVAR_PATH),
1294)     required=True,
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1295) )
1296) @click.pass_context
1297) def derivepassphrase_export_vault(
1298)     ctx: click.Context,
1299)     /,
1300)     *,
1301)     path: str | bytes | os.PathLike[str],
1302)     formats: Sequence[Literal['v0.2', 'v0.3', 'storeroom']] = (),
1303)     key: str | bytes | None = None,
1304) ) -> None:
1305)     """Export a vault-native configuration to standard output.
1306) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1307)     This is a [`click`][CLICK]-powered command-line interface function,
1308)     and not intended for programmatic use.  See the
1309)     derivepassphrase-export-vault(1) manpage for full documentation of
1310)     the interface.  (See also [`click.testing.CliRunner`][] for
1311)     controlled, programmatic invocation.)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1312) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1313)     [CLICK]: https://pypi.org/package/click/
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1314) 
1315)     """
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1316)     logger = logging.getLogger(PROG_NAME)
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1317)     if path in {'VAULT_PATH', b'VAULT_PATH'}:
1318)         path = exporter.get_vault_path()
1319)     if key is None:
1320)         key = exporter.get_vault_key()
1321)     elif isinstance(key, str):  # pragma: no branch
1322)         key = key.encode('utf-8')
1323)     for fmt in formats:
1324)         try:
1325)             config = _load_data(fmt, path, key)
1326)         except (
1327)             IsADirectoryError,
1328)             NotADirectoryError,
1329)             ValueError,
1330)             RuntimeError,
1331)         ):
Marco Ricci Replace strings in `derivep...

Marco Ricci authored 1 week ago

1332)             logger.info(
1333)                 _msg.TranslatedString(
1334)                     _msg.InfoMsgTemplate.CANNOT_LOAD_AS_VAULT_CONFIG,
1335)                     path=path,
1336)                     fmt=fmt,
1337)                 ),
1338)             )
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1339)             continue
1340)         except OSError as exc:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1341)             logger.error(
Marco Ricci Replace strings in `derivep...

Marco Ricci authored 1 week ago

1342)                 _msg.TranslatedString(
1343)                     _msg.ErrMsgTemplate.CANNOT_PARSE_AS_VAULT_CONFIG_OSERROR,
1344)                     path=path,
1345)                     error=exc.strerror,
1346)                     filename=exc.filename,
1347)                 ).maybe_without_filename(),
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1348)             )
1349)             ctx.exit(1)
1350)         except ModuleNotFoundError:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1351)             logger.error(
Marco Ricci Replace strings in `derivep...

Marco Ricci authored 1 week ago

1352)                 _msg.TranslatedString(
1353)                     _msg.ErrMsgTemplate.MISSING_MODULE,
1354)                     module='cryptography',
1355)                 ),
1356)             )
1357)             logger.info(
1358)                 _msg.TranslatedString(
1359)                     _msg.InfoMsgTemplate.PIP_INSTALL_EXTRA,
1360)                     extra_name='export',
1361)                 ),
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1362)             )
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1363)             ctx.exit(1)
1364)         else:
1365)             if not _types.is_vault_config(config):
Marco Ricci Replace strings in `derivep...

Marco Ricci authored 1 week ago

1366)                 logger.error(
1367)                     _msg.TranslatedString(
1368)                         _msg.ErrMsgTemplate.INVALID_VAULT_CONFIG,
1369)                         config=config,
1370)                     ),
1371)                 )
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1372)                 ctx.exit(1)
1373)             click.echo(json.dumps(config, indent=2, sort_keys=True))
1374)             break
1375)     else:
Marco Ricci Replace strings in `derivep...

Marco Ricci authored 1 week ago

1376)         logger.error(
1377)             _msg.TranslatedString(
1378)                 _msg.ErrMsgTemplate.CANNOT_PARSE_AS_VAULT_CONFIG,
1379)                 path=path,
1380)             ).maybe_without_filename(),
1381)         )
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

1382)         ctx.exit(1)
1383) 
1384) 
1385) # Vault
1386) # =====
1387) 
1388) 
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1389) def _config_filename(
Marco Ricci Make obtaining the compatib...

Marco Ricci authored 3 weeks ago

1390)     subsystem: str | None = 'old settings.json',
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1391) ) -> str | bytes | pathlib.Path:
1392)     """Return the filename of the configuration file for the subsystem.
1393) 
1394)     The (implicit default) file is currently named `settings.json`,
1395)     located within the configuration directory as determined by the
1396)     `DERIVEPASSPHRASE_PATH` environment variable, or by
1397)     [`click.get_app_dir`][] in POSIX mode.  Depending on the requested
1398)     subsystem, this will usually be a different file within that
1399)     directory.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1400) 
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1401)     Args:
1402)         subsystem:
1403)             Name of the configuration subsystem whose configuration
1404)             filename to return.  If not given, return the old filename
1405)             from before the subcommand migration.  If `None`, return the
1406)             configuration directory instead.
1407) 
1408)     Raises:
1409)         AssertionError:
1410)             An unknown subsystem was passed.
1411) 
1412)     Deprecated:
1413)         Since v0.2.0: The implicit default subsystem and the old
1414)         configuration filename are deprecated, and will be removed in v1.0.
1415)         The subsystem will be mandatory to specify.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1416) 
1417)     """
1418)     path: str | bytes | pathlib.Path
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1419)     path = os.getenv(PROG_NAME.upper() + '_PATH') or click.get_app_dir(
1420)         PROG_NAME, force_posix=True
1421)     )
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

1422)     # Use match/case here once Python 3.9 becomes unsupported.
1423)     if subsystem is None:
1424)         return path
Marco Ricci Make obtaining the compatib...

Marco Ricci authored 3 weeks ago

1425)     elif subsystem == 'vault':  # noqa: RET505
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

1426)         filename = f'{subsystem}.json'
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

1427)     elif subsystem == 'user configuration':
1428)         filename = 'config.toml'
Marco Ricci Make obtaining the compatib...

Marco Ricci authored 3 weeks ago

1429)     elif subsystem == 'old settings.json':
1430)         filename = 'settings.json'
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

1431)     else:  # pragma: no cover
1432)         msg = f'Unknown configuration subsystem: {subsystem!r}'
1433)         raise AssertionError(msg)
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1434)     return os.path.join(path, filename)
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1435) 
1436) 
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

1437) def _load_config() -> _types.VaultConfig:
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1438)     """Load a vault(1)-compatible config from the application directory.
1439) 
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1440)     The filename is obtained via [`_config_filename`][].  This must be
1441)     an unencrypted JSON file.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1442) 
1443)     Returns:
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1444)         The vault settings.  See [`_types.VaultConfig`][] for details.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1445) 
1446)     Raises:
1447)         OSError:
1448)             There was an OS error accessing the file.
1449)         ValueError:
1450)             The data loaded from the file is not a vault(1)-compatible
1451)             config.
1452) 
1453)     """
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1454)     filename = _config_filename(subsystem='vault')
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1455)     with open(filename, 'rb') as fileobj:
1456)         data = json.load(fileobj)
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

1457)     if not _types.is_vault_config(data):
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1458)         raise ValueError(_INVALID_VAULT_CONFIG)
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1459)     return data
1460) 
1461) 
Marco Ricci Permit one flaky test and f...

Marco Ricci authored 3 months ago

1462) def _migrate_and_load_old_config() -> tuple[
1463)     _types.VaultConfig, OSError | None
1464) ]:
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1465)     """Load and migrate a vault(1)-compatible config.
1466) 
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1467)     The (old) filename is obtained via [`_config_filename`][].  This
1468)     must be an unencrypted JSON file.  After loading, the file is
1469)     migrated to the new standard filename.
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1470) 
1471)     Returns:
1472)         The vault settings, and an optional exception encountered during
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1473)         migration.  See [`_types.VaultConfig`][] for details on the
1474)         former.
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1475) 
1476)     Raises:
1477)         OSError:
1478)             There was an OS error accessing the old file.
1479)         ValueError:
1480)             The data loaded from the file is not a vault(1)-compatible
1481)             config.
1482) 
1483)     """
1484)     new_filename = _config_filename(subsystem='vault')
Marco Ricci Make obtaining the compatib...

Marco Ricci authored 3 weeks ago

1485)     old_filename = _config_filename(subsystem='old settings.json')
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1486)     with open(old_filename, 'rb') as fileobj:
1487)         data = json.load(fileobj)
1488)     if not _types.is_vault_config(data):
1489)         raise ValueError(_INVALID_VAULT_CONFIG)
1490)     try:
1491)         os.replace(old_filename, new_filename)
1492)     except OSError as exc:
1493)         return data, exc
1494)     else:
1495)         return data, None
1496) 
1497) 
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

1498) def _save_config(config: _types.VaultConfig, /) -> None:
Marco Ricci Create the configuration di...

Marco Ricci authored 5 months ago

1499)     """Save a vault(1)-compatible config to the application directory.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1500) 
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1501)     The filename is obtained via [`_config_filename`][].  The config
1502)     will be stored as an unencrypted JSON file.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1503) 
1504)     Args:
1505)         config:
1506)             vault configuration to save.
1507) 
1508)     Raises:
1509)         OSError:
1510)             There was an OS error accessing or writing the file.
1511)         ValueError:
1512)             The data cannot be stored as a vault(1)-compatible config.
1513) 
1514)     """
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

1515)     if not _types.is_vault_config(config):
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1516)         raise ValueError(_INVALID_VAULT_CONFIG)
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

1517)     filename = _config_filename(subsystem='vault')
Marco Ricci Create the configuration di...

Marco Ricci authored 5 months ago

1518)     filedir = os.path.dirname(os.path.abspath(filename))
1519)     try:
1520)         os.makedirs(filedir, exist_ok=False)
1521)     except FileExistsError:
1522)         if not os.path.isdir(filedir):
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

1523)             raise  # noqa: DOC501
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1524)     with open(filename, 'w', encoding='UTF-8') as fileobj:
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1525)         json.dump(config, fileobj)
1526) 
1527) 
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

1528) def _load_user_config() -> dict[str, Any]:
1529)     """Load the user config from the application directory.
1530) 
1531)     The filename is obtained via [`_config_filename`][].
1532) 
1533)     Returns:
1534)         The user configuration, as a nested `dict`.
1535) 
1536)     Raises:
1537)         OSError:
1538)             There was an OS error accessing the file.
1539)         ValueError:
1540)             The data loaded from the file is not a valid configuration
1541)             file.
1542) 
1543)     """
1544)     filename = _config_filename(subsystem='user configuration')
1545)     with open(filename, 'rb') as fileobj:
1546)         return tomllib.load(fileobj)
1547) 
1548) 
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1549) def _get_suitable_ssh_keys(
Marco Ricci Move `sequin` and `ssh_agen...

Marco Ricci authored 5 months ago

1550)     conn: ssh_agent.SSHAgentClient | socket.socket | None = None, /
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

1551) ) -> Iterator[_types.KeyCommentPair]:
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1552)     """Yield all SSH keys suitable for passphrase derivation.
1553) 
1554)     Suitable SSH keys are queried from the running SSH agent (see
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1555)     [`ssh_agent.SSHAgentClient.list_keys`][]).
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1556) 
1557)     Args:
1558)         conn:
Marco Ricci Support one-off SSH agent c...

Marco Ricci authored 1 month ago

1559)             An optional connection hint to the SSH agent.  See
1560)             [`ssh_agent.SSHAgentClient.ensure_agent_subcontext`][].
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1561) 
1562)     Yields:
Marco Ricci Convert old syntax for Yiel...

Marco Ricci authored 3 months ago

1563)         Every SSH key from the SSH agent that is suitable for passphrase
1564)         derivation.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1565) 
1566)     Raises:
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

1567)         KeyError:
1568)             `conn` was `None`, and the `SSH_AUTH_SOCK` environment
1569)             variable was not found.
Marco Ricci Fail gracefully if UNIX dom...

Marco Ricci authored 3 months ago

1570)         NotImplementedError:
1571)             `conn` was `None`, and this Python does not support
1572)             [`socket.AF_UNIX`][], so the SSH agent client cannot be
1573)             automatically set up.
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

1574)         OSError:
1575)             `conn` was a socket or `None`, and there was an error
1576)             setting up a socket connection to the agent.
Marco Ricci Distinguish between a key l...

Marco Ricci authored 6 months ago

1577)         LookupError:
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1578)             No keys usable for passphrase derivation are loaded into the
1579)             SSH agent.
Marco Ricci Distinguish between a key l...

Marco Ricci authored 6 months ago

1580)         RuntimeError:
1581)             There was an error communicating with the SSH agent.
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 3 months ago

1582)         ssh_agent.SSHAgentFailedError:
Marco Ricci Add a specific error class...

Marco Ricci authored 4 months ago

1583)             The agent failed to supply a list of loaded keys.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1584) 
1585)     """
Marco Ricci Support one-off SSH agent c...

Marco Ricci authored 1 month ago

1586)     with ssh_agent.SSHAgentClient.ensure_agent_subcontext(conn) as client:
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1587)         try:
1588)             all_key_comment_pairs = list(client.list_keys())
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

1589)         except EOFError as exc:  # pragma: no cover
1590)             raise RuntimeError(_AGENT_COMMUNICATION_ERROR) from exc
Marco Ricci Publish polished `is_suitab...

Marco Ricci authored 1 month ago

1591)         suitable_keys = copy.copy(all_key_comment_pairs)
1592)         for pair in all_key_comment_pairs:
1593)             key, _comment = pair
1594)             if vault.Vault.is_suitable_ssh_key(key, client=client):
1595)                 yield pair
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1596)     if not suitable_keys:  # pragma: no cover
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1597)         raise LookupError(_NO_SUITABLE_KEYS)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1598) 
1599) 
1600) def _prompt_for_selection(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1601)     items: Sequence[str | bytes],
1602)     heading: str = 'Possible choices:',
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1603)     single_choice_prompt: str = 'Confirm this choice?',
1604) ) -> int:
1605)     """Prompt user for a choice among the given items.
1606) 
1607)     Print the heading, if any, then present the items to the user.  If
1608)     there are multiple items, prompt the user for a selection, validate
1609)     the choice, then return the list index of the selected item.  If
1610)     there is only a single item, request confirmation for that item
1611)     instead, and return the correct index.
1612) 
1613)     Args:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

1614)         items:
1615)             The list of items to choose from.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1616)         heading:
1617)             A heading for the list of items, to print immediately
1618)             before.  Defaults to a reasonable standard heading.  If
1619)             explicitly empty, print no heading.
1620)         single_choice_prompt:
1621)             The confirmation prompt if there is only a single possible
1622)             choice.  Defaults to a reasonable standard prompt.
1623) 
1624)     Returns:
1625)         An index into the items sequence, indicating the user's
1626)         selection.
1627) 
1628)     Raises:
1629)         IndexError:
1630)             The user made an invalid or empty selection, or requested an
1631)             abort.
1632) 
1633)     """
1634)     n = len(items)
1635)     if heading:
1636)         click.echo(click.style(heading, bold=True))
1637)     for i, x in enumerate(items, start=1):
1638)         click.echo(click.style(f'[{i}]', bold=True), nl=False)
1639)         click.echo(' ', nl=False)
1640)         click.echo(x)
1641)     if n > 1:
1642)         choices = click.Choice([''] + [str(i) for i in range(1, n + 1)])
1643)         choice = click.prompt(
1644)             f'Your selection? (1-{n}, leave empty to abort)',
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1645)             err=True,
1646)             type=choices,
1647)             show_choices=False,
1648)             show_default=False,
1649)             default='',
1650)         )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1651)         if not choice:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1652)             raise IndexError(_EMPTY_SELECTION)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1653)         return int(choice) - 1
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1654)     prompt_suffix = (
1655)         ' ' if single_choice_prompt.endswith(tuple('?.!')) else ': '
1656)     )
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1657)     try:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1658)         click.confirm(
1659)             single_choice_prompt,
1660)             prompt_suffix=prompt_suffix,
1661)             err=True,
1662)             abort=True,
1663)             default=False,
1664)             show_default=False,
1665)         )
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

1666)     except click.Abort:
1667)         raise IndexError(_EMPTY_SELECTION) from None
1668)     return 0
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1669) 
1670) 
1671) def _select_ssh_key(
Marco Ricci Move `sequin` and `ssh_agen...

Marco Ricci authored 5 months ago

1672)     conn: ssh_agent.SSHAgentClient | socket.socket | None = None, /
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1673) ) -> bytes | bytearray:
1674)     """Interactively select an SSH key for passphrase derivation.
1675) 
1676)     Suitable SSH keys are queried from the running SSH agent (see
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 3 months ago

1677)     [`ssh_agent.SSHAgentClient.list_keys`][]), then the user is prompted
1678)     interactively (see [`click.prompt`][]) for a selection.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1679) 
1680)     Args:
1681)         conn:
Marco Ricci Support one-off SSH agent c...

Marco Ricci authored 1 month ago

1682)             An optional connection hint to the SSH agent.  See
1683)             [`ssh_agent.SSHAgentClient.ensure_agent_subcontext`][].
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1684) 
1685)     Returns:
1686)         The selected SSH key.
1687) 
1688)     Raises:
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

1689)         KeyError:
1690)             `conn` was `None`, and the `SSH_AUTH_SOCK` environment
1691)             variable was not found.
Marco Ricci Fail gracefully if UNIX dom...

Marco Ricci authored 3 months ago

1692)         NotImplementedError:
1693)             `conn` was `None`, and this Python does not support
1694)             [`socket.AF_UNIX`][], so the SSH agent client cannot be
1695)             automatically set up.
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

1696)         OSError:
1697)             `conn` was a socket or `None`, and there was an error
1698)             setting up a socket connection to the agent.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1699)         IndexError:
1700)             The user made an invalid or empty selection, or requested an
1701)             abort.
Marco Ricci Distinguish between a key l...

Marco Ricci authored 6 months ago

1702)         LookupError:
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1703)             No keys usable for passphrase derivation are loaded into the
1704)             SSH agent.
Marco Ricci Distinguish between a key l...

Marco Ricci authored 6 months ago

1705)         RuntimeError:
1706)             There was an error communicating with the SSH agent.
Marco Ricci Add a specific error class...

Marco Ricci authored 4 months ago

1707)         SSHAgentFailedError:
1708)             The agent failed to supply a list of loaded keys.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1709)     """
1710)     suitable_keys = list(_get_suitable_ssh_keys(conn))
1711)     key_listing: list[str] = []
Marco Ricci Move `sequin` and `ssh_agen...

Marco Ricci authored 5 months ago

1712)     unstring_prefix = ssh_agent.SSHAgentClient.unstring_prefix
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1713)     for key, comment in suitable_keys:
1714)         keytype = unstring_prefix(key)[0].decode('ASCII')
1715)         key_str = base64.standard_b64encode(key).decode('ASCII')
Marco Ricci Make suitable SSH key listi...

Marco Ricci authored 1 month ago

1716)         remaining_key_display_length = KEY_DISPLAY_LENGTH - 1 - len(keytype)
1717)         key_extract = min(
1718)             key_str,
1719)             '...' + key_str[-remaining_key_display_length:],
1720)             key=len,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1721)         )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1722)         comment_str = comment.decode('UTF-8', errors='replace')
Marco Ricci Make suitable SSH key listi...

Marco Ricci authored 1 month ago

1723)         key_listing.append(f'{keytype} {key_extract}  {comment_str}')
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1724)     choice = _prompt_for_selection(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1725)         key_listing,
1726)         heading='Suitable SSH keys:',
1727)         single_choice_prompt='Use this key?',
1728)     )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1729)     return suitable_keys[choice].key
1730) 
1731) 
1732) def _prompt_for_passphrase() -> str:
1733)     """Interactively prompt for the passphrase.
1734) 
1735)     Calls [`click.prompt`][] internally.  Moved into a separate function
1736)     mainly for testing/mocking purposes.
1737) 
1738)     Returns:
1739)         The user input.
1740) 
1741)     """
Marco Ricci Fix typing issues in mypy s...

Marco Ricci authored 5 months ago

1742)     return cast(
1743)         str,
1744)         click.prompt(
1745)             'Passphrase',
1746)             default='',
1747)             hide_input=True,
1748)             show_default=False,
1749)             err=True,
1750)         ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1751)     )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

1752) 
1753) 
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1754) def _toml_key(*parts: str) -> str:
1755)     """Return a formatted TOML key, given its parts."""
Marco Ricci Fix formatting, some covera...

Marco Ricci authored 2 weeks ago

1756) 
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1757)     def escape(string: str) -> str:
1758)         translated = string.translate({
1759)             0: r'\u0000',
1760)             1: r'\u0001',
1761)             2: r'\u0002',
1762)             3: r'\u0003',
1763)             4: r'\u0004',
1764)             5: r'\u0005',
1765)             6: r'\u0006',
1766)             7: r'\u0007',
1767)             8: r'\b',
1768)             9: r'\t',
1769)             10: r'\n',
1770)             11: r'\u000B',
1771)             12: r'\f',
1772)             13: r'\r',
1773)             14: r'\u000E',
1774)             15: r'\u000F',
1775)             ord('"'): r'\"',
1776)             ord('\\'): r'\\',
1777)             127: r'\u007F',
1778)         })
1779)         return f'"{translated}"' if translated != string else string
Marco Ricci Fix formatting, some covera...

Marco Ricci authored 2 weeks ago

1780) 
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1781)     return '.'.join(map(escape, parts))
1782) 
1783) 
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

1784) class _ORIGIN(enum.Enum):
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1785)     INTERACTIVE: str = 'interactive input'
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

1786) 
1787) 
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1788) def _check_for_misleading_passphrase(
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

1789)     key: tuple[str, ...] | _ORIGIN,
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1790)     value: dict[str, Any],
1791)     *,
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1792)     main_config: dict[str, Any],
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1793) ) -> None:
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1794)     form_key = 'unicode-normalization-form'
1795)     default_form: str = main_config.get('vault', {}).get(
1796)         f'default-{form_key}', 'NFC'
1797)     )
1798)     form_dict: dict[str, dict] = main_config.get('vault', {}).get(form_key, {})
1799)     form: Any = (
1800)         default_form
1801)         if isinstance(key, _ORIGIN) or key == ('global',)
1802)         else form_dict.get(key[1], default_form)
1803)     )
1804)     config_key = (
1805)         _toml_key('vault', key[1], form_key)
1806)         if isinstance(key, tuple) and len(key) > 1 and key[1] in form_dict
1807)         else f'vault.default-{form_key}'
1808)     )
1809)     if form not in {'NFC', 'NFD', 'NFKC', 'NFKD'}:
1810)         msg = f'Invalid value {form!r} for config key {config_key}'
1811)         raise AssertionError(msg)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1812)     logger = logging.getLogger(PROG_NAME)
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1813)     formatted_key = (
1814)         key.value if isinstance(key, _ORIGIN) else _types.json_path(key)
1815)     )
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1816)     if 'phrase' in value:
1817)         phrase = value['phrase']
1818)         if not unicodedata.is_normalized(form, phrase):
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1819)             logger.warning(
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1820)                 (
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1821)                     'The %s passphrase is not %s-normalized.  Its '
1822)                     'serialization as a byte string may not be what you '
1823)                     'expect it to be, even if it *displays* correctly.  '
1824)                     'Please make sure to double-check any derived '
1825)                     'passphrases for unexpected results.'
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1826)                 ),
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

1827)                 formatted_key,
1828)                 form,
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

1829)                 stacklevel=2,
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

1830)             )
1831) 
1832) 
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1833) def _key_to_phrase(
1834)     key_: str | bytes | bytearray,
1835)     /,
1836)     *,
1837)     error_callback: Callable[..., NoReturn] = sys.exit,
1838) ) -> bytes | bytearray:
1839)     key = base64.standard_b64decode(key_)
1840)     try:
1841)         with ssh_agent.SSHAgentClient.ensure_agent_subcontext() as client:
1842)             try:
1843)                 return vault.Vault.phrase_from_key(key, conn=client)
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

1844)             except ssh_agent.SSHAgentFailedError as exc:
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1845)                 try:
1846)                     keylist = client.list_keys()
1847)                 except ssh_agent.SSHAgentFailedError:
1848)                     pass
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

1849)                 except Exception as exc2:  # noqa: BLE001
1850)                     exc.__context__ = exc2
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1851)                 else:
1852)                     if not any(  # pragma: no branch
1853)                         k == key for k, _ in keylist
1854)                     ):
1855)                         error_callback(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1856)                             _msg.TranslatedString(
1857)                                 _msg.ErrMsgTemplate.SSH_KEY_NOT_LOADED
1858)                             )
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1859)                         )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1860)                 error_callback(
1861)                     _msg.TranslatedString(
1862)                         _msg.ErrMsgTemplate.AGENT_REFUSED_SIGNATURE
1863)                     ),
1864)                     exc_info=exc,
1865)                 )
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1866)     except KeyError:
1867)         error_callback(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1868)             _msg.TranslatedString(_msg.ErrMsgTemplate.NO_SSH_AGENT_FOUND)
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1869)         )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1870)     except NotImplementedError:
1871)         error_callback(_msg.TranslatedString(_msg.ErrMsgTemplate.NO_AF_UNIX))
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

1872)     except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1873)         error_callback(
1874)             _msg.TranslatedString(
1875)                 _msg.ErrMsgTemplate.CANNOT_CONNECT_TO_AGENT,
1876)                 error=exc.strerror,
1877)                 filename=exc.filename,
1878)             ).maybe_without_filename()
1879)         )
1880)     except RuntimeError as exc:
1881)         error_callback(
1882)             _msg.TranslatedString(_msg.ErrMsgTemplate.CANNOT_UNDERSTAND_AGENT),
1883)             exc_info=exc,
1884)         )
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

1885) 
1886) 
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

1887) def _print_config_as_sh_script(
1888)     config: _types.VaultConfig,
1889)     /,
1890)     *,
1891)     outfile: TextIO,
1892)     prog_name_list: Sequence[str],
1893) ) -> None:
1894)     service_keys = (
1895)         'length',
1896)         'repeat',
1897)         'lower',
1898)         'upper',
1899)         'number',
1900)         'space',
1901)         'dash',
1902)         'symbol',
1903)     )
1904)     print('#!/bin/sh -e', file=outfile)
1905)     print(file=outfile)
1906)     print(shlex.join([*prog_name_list, '--clear']), file=outfile)
1907)     sv_obj_pairs: list[
1908)         tuple[
1909)             str | None,
1910)             _types.VaultConfigGlobalSettings
1911)             | _types.VaultConfigServicesSettings,
1912)         ],
1913)     ] = list(config['services'].items())
1914)     if config.get('global', {}):
1915)         sv_obj_pairs.insert(0, (None, config['global']))
1916)     for sv, sv_obj in sv_obj_pairs:
1917)         this_service_keys = tuple(k for k in service_keys if k in sv_obj)
1918)         this_other_keys = tuple(k for k in sv_obj if k not in service_keys)
1919)         if this_other_keys:
1920)             other_sv_obj = {k: sv_obj[k] for k in this_other_keys}  # type: ignore[literal-required]
1921)             dumped_config = json.dumps(
1922)                 (
1923)                     {'services': {sv: other_sv_obj}}
1924)                     if sv is not None
1925)                     else {'global': other_sv_obj, 'services': {}}
1926)                 ),
1927)                 ensure_ascii=False,
1928)                 indent=None,
1929)             )
1930)             print(
1931)                 shlex.join([*prog_name_list, '--import', '-']) + " <<'HERE'",
1932)                 dumped_config,
1933)                 'HERE',
1934)                 sep='\n',
1935)                 file=outfile,
1936)             )
1937)         if not this_service_keys and not this_other_keys and sv:
1938)             dumped_config = json.dumps(
1939)                 {'services': {sv: {}}},
1940)                 ensure_ascii=False,
1941)                 indent=None,
1942)             )
1943)             print(
1944)                 shlex.join([*prog_name_list, '--import', '-']) + " <<'HERE'",
1945)                 dumped_config,
1946)                 'HERE',
1947)                 sep='\n',
1948)                 file=outfile,
1949)             )
1950)         elif this_service_keys:
1951)             tokens = [*prog_name_list, '--config']
1952)             for key in this_service_keys:
1953)                 tokens.extend([f'--{key}', str(sv_obj[key])])  # type: ignore[literal-required]
1954)             if sv is not None:
1955)                 tokens.extend(['--', sv])
1956)             print(shlex.join(tokens), file=outfile)
1957) 
1958) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

1959) # Concrete option groups used by this command-line interface.
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

1960) class PassphraseGenerationOption(OptionGroupOption):
1961)     """Passphrase generation options for the CLI."""
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1962) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1963)     option_group_name = _msg.TranslatedString(
1964)         _msg.Label.PASSPHRASE_GENERATION_LABEL
1965)     )
1966)     epilog = _msg.TranslatedString(
1967)         _msg.Label.PASSPHRASE_GENERATION_EPILOG,
1968)         metavar=_msg.TranslatedString(
1969)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
1970)         ),
1971)     )
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1972) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

1973) 
1974) class ConfigurationOption(OptionGroupOption):
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1975)     """Configuration options for the CLI."""
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1976) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1977)     option_group_name = _msg.TranslatedString(_msg.Label.CONFIGURATION_LABEL)
1978)     epilog = _msg.TranslatedString(_msg.Label.CONFIGURATION_EPILOG)
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1979) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

1980) 
1981) class StorageManagementOption(OptionGroupOption):
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

1982)     """Storage management options for the CLI."""
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1983) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1984)     option_group_name = _msg.TranslatedString(
1985)         _msg.Label.STORAGE_MANAGEMENT_LABEL
1986)     )
1987)     epilog = _msg.TranslatedString(
1988)         _msg.Label.STORAGE_MANAGEMENT_EPILOG,
1989)         metavar=_msg.TranslatedString(
1990)             _msg.Label.STORAGE_MANAGEMENT_METAVAR_PATH
1991)         ),
1992)     )
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

1993) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

1994) 
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

1995) class CompatibilityOption(OptionGroupOption):
1996)     """Compatibility and incompatibility options for the CLI."""
1997) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

1998)     option_group_name = _msg.TranslatedString(
1999)         _msg.Label.COMPATIBILITY_OPTION_LABEL
2000)     )
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2001) 
2002) 
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2003) def _validate_occurrence_constraint(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2004)     ctx: click.Context,
2005)     param: click.Parameter,
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2006)     value: Any,  # noqa: ANN401
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2007) ) -> int | None:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2008)     """Check that the occurrence constraint is valid (int, 0 or larger).
2009) 
2010)     Args:
2011)         ctx: The `click` context.
2012)         param: The current command-line parameter.
2013)         value: The parameter value to be checked.
2014) 
2015)     Returns:
2016)         The parsed parameter value.
2017) 
2018)     Raises:
2019)         click.BadParameter: The parameter value is invalid.
2020) 
2021)     """
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2022)     del ctx  # Unused.
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

2023)     del param  # Unused.
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2024)     if value is None:
2025)         return value
2026)     if isinstance(value, int):
2027)         int_value = value
2028)     else:
2029)         try:
2030)             int_value = int(value, 10)
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2031)         except ValueError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2032)             raise click.BadParameter(_NOT_AN_INTEGER) from exc
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2033)     if int_value < 0:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2034)         raise click.BadParameter(_NOT_A_NONNEGATIVE_INTEGER)
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2035)     return int_value
2036) 
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2037) 
2038) def _validate_length(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2039)     ctx: click.Context,
2040)     param: click.Parameter,
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2041)     value: Any,  # noqa: ANN401
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2042) ) -> int | None:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2043)     """Check that the length is valid (int, 1 or larger).
2044) 
2045)     Args:
2046)         ctx: The `click` context.
2047)         param: The current command-line parameter.
2048)         value: The parameter value to be checked.
2049) 
2050)     Returns:
2051)         The parsed parameter value.
2052) 
2053)     Raises:
2054)         click.BadParameter: The parameter value is invalid.
2055) 
2056)     """
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2057)     del ctx  # Unused.
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

2058)     del param  # Unused.
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2059)     if value is None:
2060)         return value
2061)     if isinstance(value, int):
2062)         int_value = value
2063)     else:
2064)         try:
2065)             int_value = int(value, 10)
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2066)         except ValueError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2067)             raise click.BadParameter(_NOT_AN_INTEGER) from exc
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2068)     if int_value < 1:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2069)         raise click.BadParameter(_NOT_A_POSITIVE_INTEGER)
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2070)     return int_value
2071) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2072) 
2073) DEFAULT_NOTES_TEMPLATE = """\
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2074) # Enter notes below the line with the cut mark (ASCII scissors and
2075) # dashes).  Lines above the cut mark (such as this one) will be ignored.
2076) #
2077) # If you wish to clear the notes, leave everything beyond the cut mark
2078) # blank.  However, if you leave the *entire* file blank, also removing
2079) # the cut mark, then the edit is aborted, and the old notes contents are
2080) # retained.
2081) #
2082) # - - - - - >8 - - - - - >8 - - - - - >8 - - - - - >8 - - - - -
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2083) """
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2084) DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -'
2085) 
2086) 
Marco Ricci Reimplement deprecated subc...

Marco Ricci authored 1 month ago

2087) @derivepassphrase.command(
2088)     'vault',
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2089)     context_settings={'help_option_names': ['-h', '--help']},
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2090)     cls=CommandWithHelpGroups,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2091)     help=(
2092)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_VAULT_01),
2093)         _msg.TranslatedString(
2094)             _msg.Label.DERIVEPASSPHRASE_VAULT_02,
2095)             service_metavar=_msg.TranslatedString(
2096)                 _msg.Label.VAULT_METAVAR_SERVICE
2097)             ),
2098)         ),
2099)     ),
2100)     epilog=(
2101)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_VAULT_EPILOG_01),
2102)         _msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_VAULT_EPILOG_02),
2103)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2104) )
2105) @click.option(
2106)     '-p',
2107)     '--phrase',
2108)     'use_phrase',
2109)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2110)     help=_msg.TranslatedString(
2111)         _msg.Label.DERIVEPASSPHRASE_VAULT_PHRASE_HELP_TEXT
2112)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2113)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2114) )
2115) @click.option(
2116)     '-k',
2117)     '--key',
2118)     'use_key',
2119)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2120)     help=_msg.TranslatedString(
2121)         _msg.Label.DERIVEPASSPHRASE_VAULT_KEY_HELP_TEXT
2122)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2123)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2124) )
2125) @click.option(
2126)     '-l',
2127)     '--length',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2128)     metavar=_msg.TranslatedString(
2129)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2130)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2131)     callback=_validate_length,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2132)     help=_msg.TranslatedString(
2133)         _msg.Label.DERIVEPASSPHRASE_VAULT_LENGTH_HELP_TEXT,
2134)         metavar=_msg.TranslatedString(
2135)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2136)         ),
2137)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2138)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2139) )
2140) @click.option(
2141)     '-r',
2142)     '--repeat',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2143)     metavar=_msg.TranslatedString(
2144)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2145)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2146)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2147)     help=_msg.TranslatedString(
2148)         _msg.Label.DERIVEPASSPHRASE_VAULT_REPEAT_HELP_TEXT,
2149)         metavar=_msg.TranslatedString(
2150)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2151)         ),
2152)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2153)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2154) )
2155) @click.option(
2156)     '--lower',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2157)     metavar=_msg.TranslatedString(
2158)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2159)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2160)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2161)     help=_msg.TranslatedString(
2162)         _msg.Label.DERIVEPASSPHRASE_VAULT_LOWER_HELP_TEXT,
2163)         metavar=_msg.TranslatedString(
2164)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2165)         ),
2166)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2167)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2168) )
2169) @click.option(
2170)     '--upper',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2171)     metavar=_msg.TranslatedString(
2172)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2173)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2174)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2175)     help=_msg.TranslatedString(
2176)         _msg.Label.DERIVEPASSPHRASE_VAULT_UPPER_HELP_TEXT,
2177)         metavar=_msg.TranslatedString(
2178)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2179)         ),
2180)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2181)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2182) )
2183) @click.option(
2184)     '--number',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2185)     metavar=_msg.TranslatedString(
2186)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2187)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2188)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2189)     help=_msg.TranslatedString(
2190)         _msg.Label.DERIVEPASSPHRASE_VAULT_NUMBER_HELP_TEXT,
2191)         metavar=_msg.TranslatedString(
2192)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2193)         ),
2194)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2195)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2196) )
2197) @click.option(
2198)     '--space',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2199)     metavar=_msg.TranslatedString(
2200)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2201)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2202)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2203)     help=_msg.TranslatedString(
2204)         _msg.Label.DERIVEPASSPHRASE_VAULT_SPACE_HELP_TEXT,
2205)         metavar=_msg.TranslatedString(
2206)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2207)         ),
2208)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2209)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2210) )
2211) @click.option(
2212)     '--dash',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2213)     metavar=_msg.TranslatedString(
2214)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2215)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2216)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2217)     help=_msg.TranslatedString(
2218)         _msg.Label.DERIVEPASSPHRASE_VAULT_DASH_HELP_TEXT,
2219)         metavar=_msg.TranslatedString(
2220)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2221)         ),
2222)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2223)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2224) )
2225) @click.option(
2226)     '--symbol',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2227)     metavar=_msg.TranslatedString(
2228)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2229)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2230)     callback=_validate_occurrence_constraint,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2231)     help=_msg.TranslatedString(
2232)         _msg.Label.DERIVEPASSPHRASE_VAULT_SYMBOL_HELP_TEXT,
2233)         metavar=_msg.TranslatedString(
2234)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2235)         ),
2236)     ),
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2237)     cls=PassphraseGenerationOption,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2238) )
2239) @click.option(
2240)     '-n',
2241)     '--notes',
2242)     'edit_notes',
2243)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2244)     help=_msg.TranslatedString(
2245)         _msg.Label.DERIVEPASSPHRASE_VAULT_NOTES_HELP_TEXT,
2246)         service_metavar=_msg.TranslatedString(
2247)             _msg.Label.VAULT_METAVAR_SERVICE
2248)         ),
2249)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2250)     cls=ConfigurationOption,
2251) )
2252) @click.option(
2253)     '-c',
2254)     '--config',
2255)     'store_config_only',
2256)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2257)     help=_msg.TranslatedString(
2258)         _msg.Label.DERIVEPASSPHRASE_VAULT_CONFIG_HELP_TEXT,
2259)         service_metavar=_msg.TranslatedString(
2260)             _msg.Label.VAULT_METAVAR_SERVICE
2261)         ),
2262)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2263)     cls=ConfigurationOption,
2264) )
2265) @click.option(
2266)     '-x',
2267)     '--delete',
2268)     'delete_service_settings',
2269)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2270)     help=_msg.TranslatedString(
2271)         _msg.Label.DERIVEPASSPHRASE_VAULT_DELETE_HELP_TEXT,
2272)         service_metavar=_msg.TranslatedString(
2273)             _msg.Label.VAULT_METAVAR_SERVICE
2274)         ),
2275)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2276)     cls=ConfigurationOption,
2277) )
2278) @click.option(
2279)     '--delete-globals',
2280)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2281)     help=_msg.TranslatedString(
2282)         _msg.Label.DERIVEPASSPHRASE_VAULT_DELETE_GLOBALS_HELP_TEXT,
2283)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2284)     cls=ConfigurationOption,
2285) )
2286) @click.option(
2287)     '-X',
2288)     '--clear',
2289)     'clear_all_settings',
2290)     is_flag=True,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2291)     help=_msg.TranslatedString(
2292)         _msg.Label.DERIVEPASSPHRASE_VAULT_DELETE_ALL_HELP_TEXT,
2293)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2294)     cls=ConfigurationOption,
2295) )
2296) @click.option(
2297)     '-e',
2298)     '--export',
2299)     'export_settings',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2300)     metavar=_msg.TranslatedString(
2301)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2302)     ),
2303)     help=_msg.TranslatedString(
2304)         _msg.Label.DERIVEPASSPHRASE_VAULT_EXPORT_HELP_TEXT,
2305)         metavar=_msg.TranslatedString(
2306)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2307)         ),
2308)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2309)     cls=StorageManagementOption,
2310) )
2311) @click.option(
2312)     '-i',
2313)     '--import',
2314)     'import_settings',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2315)     metavar=_msg.TranslatedString(
2316)         _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2317)     ),
2318)     help=_msg.TranslatedString(
2319)         _msg.Label.DERIVEPASSPHRASE_VAULT_IMPORT_HELP_TEXT,
2320)         metavar=_msg.TranslatedString(
2321)             _msg.Label.PASSPHRASE_GENERATION_METAVAR_NUMBER
2322)         ),
2323)     ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2324)     cls=StorageManagementOption,
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2325) )
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2326) @click.option(
2327)     '--overwrite-existing/--merge-existing',
2328)     'overwrite_config',
2329)     default=False,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2330)     help=_msg.TranslatedString(
2331)         _msg.Label.DERIVEPASSPHRASE_VAULT_OVERWRITE_HELP_TEXT
2332)     ),
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2333)     cls=CompatibilityOption,
2334) )
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

2335) @click.option(
2336)     '--unset',
2337)     'unset_settings',
2338)     multiple=True,
2339)     type=click.Choice([
2340)         'phrase',
2341)         'key',
2342)         'length',
2343)         'repeat',
2344)         'lower',
2345)         'upper',
2346)         'number',
2347)         'space',
2348)         'dash',
2349)         'symbol',
2350)     ]),
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2351)     help=_msg.TranslatedString(
2352)         _msg.Label.DERIVEPASSPHRASE_VAULT_UNSET_HELP_TEXT
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

2353)     ),
2354)     cls=CompatibilityOption,
2355) )
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

2356) @click.option(
2357)     '--export-as',
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2358)     type=click.Choice(['json', 'sh']),
2359)     default='json',
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2360)     help=_msg.TranslatedString(
2361)         _msg.Label.DERIVEPASSPHRASE_VAULT_EXPORT_AS_HELP_TEXT
2362)     ),
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

2363)     cls=CompatibilityOption,
2364) )
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

2365) @click.version_option(version=dpp.__version__, prog_name=PROG_NAME)
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2366) @standard_logging_options
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2367) @click.argument(
2368)     'service',
2369)     metavar=_msg.TranslatedString(_msg.Label.VAULT_METAVAR_SERVICE),
2370)     required=False,
2371)     default=None,
2372) )
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2373) @click.pass_context
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

2374) def derivepassphrase_vault(  # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2375)     ctx: click.Context,
2376)     /,
2377)     *,
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2378)     service: str | None = None,
2379)     use_phrase: bool = False,
2380)     use_key: bool = False,
2381)     length: int | None = None,
2382)     repeat: int | None = None,
2383)     lower: int | None = None,
2384)     upper: int | None = None,
2385)     number: int | None = None,
2386)     space: int | None = None,
2387)     dash: int | None = None,
2388)     symbol: int | None = None,
2389)     edit_notes: bool = False,
2390)     store_config_only: bool = False,
2391)     delete_service_settings: bool = False,
2392)     delete_globals: bool = False,
2393)     clear_all_settings: bool = False,
2394)     export_settings: TextIO | pathlib.Path | os.PathLike[str] | None = None,
2395)     import_settings: TextIO | pathlib.Path | os.PathLike[str] | None = None,
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2396)     overwrite_config: bool = False,
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

2397)     unset_settings: Sequence[str] = (),
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

2398)     export_as: Literal['json', 'sh'] = 'json',
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2399) ) -> None:
Marco Ricci Reintegrate all functionali...

Marco Ricci authored 3 months ago

2400)     """Derive a passphrase using the vault(1) derivation scheme.
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2401) 
2402)     This is a [`click`][CLICK]-powered command-line interface function,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2403)     and not intended for programmatic use.  See the
2404)     derivepassphrase-vault(1) manpage for full documentation of the
2405)     interface.  (See also [`click.testing.CliRunner`][] for controlled,
2406)     programmatic invocation.)
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2407) 
Marco Ricci Update all URLs to stable a...

Marco Ricci authored 3 months ago

2408)     [CLICK]: https://pypi.org/package/click/
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2409) 
2410)     Parameters:
2411)         ctx (click.Context):
2412)             The `click` context.
2413) 
2414)     Other Parameters:
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2415)         service:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2416)             A service name.  Required, unless operating on global
2417)             settings or importing/exporting settings.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2418)         use_phrase:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2419)             Command-line argument `-p`/`--phrase`.  If given, query the
2420)             user for a passphrase instead of an SSH key.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2421)         use_key:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2422)             Command-line argument `-k`/`--key`.  If given, query the
2423)             user for an SSH key instead of a passphrase.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2424)         length:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2425)             Command-line argument `-l`/`--length`.  Override the default
2426)             length of the generated passphrase.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2427)         repeat:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2428)             Command-line argument `-r`/`--repeat`.  Override the default
2429)             repetition limit if positive, or disable the repetition
2430)             limit if 0.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2431)         lower:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2432)             Command-line argument `--lower`.  Require a given amount of
2433)             ASCII lowercase characters if positive, else forbid ASCII
2434)             lowercase characters if 0.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2435)         upper:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2436)             Command-line argument `--upper`.  Same as `lower`, but for
2437)             ASCII uppercase characters.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2438)         number:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2439)             Command-line argument `--number`.  Same as `lower`, but for
2440)             ASCII digits.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2441)         space:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2442)             Command-line argument `--space`.  Same as `lower`, but for
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2443)             the space character.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2444)         dash:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2445)             Command-line argument `--dash`.  Same as `lower`, but for
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2446)             the hyphen-minus and underscore characters.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2447)         symbol:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2448)             Command-line argument `--symbol`.  Same as `lower`, but for
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2449)             all other ASCII printable characters (except backquote).
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2450)         edit_notes:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2451)             Command-line argument `-n`/`--notes`.  If given, spawn an
2452)             editor to edit notes for `service`.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2453)         store_config_only:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2454)             Command-line argument `-c`/`--config`.  If given, saves the
2455)             other given settings (`--key`, ..., `--symbol`) to the
2456)             configuration file, either specifically for `service` or as
2457)             global settings.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2458)         delete_service_settings:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2459)             Command-line argument `-x`/`--delete`.  If given, removes
2460)             the settings for `service` from the configuration file.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2461)         delete_globals:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2462)             Command-line argument `--delete-globals`.  If given, removes
2463)             the global settings from the configuration file.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2464)         clear_all_settings:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2465)             Command-line argument `-X`/`--clear`.  If given, removes all
2466)             settings from the configuration file.
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2467)         export_settings:
2468)             Command-line argument `-e`/`--export`.  If a file object,
2469)             then it must be open for writing and accept `str` inputs.
2470)             Otherwise, a filename to open for writing.  Using `-` for
2471)             standard output is supported.
2472)         import_settings:
2473)             Command-line argument `-i`/`--import`.  If a file object, it
2474)             must be open for reading and yield `str` values.  Otherwise,
2475)             a filename to open for reading.  Using `-` for standard
2476)             input is supported.
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2477)         overwrite_config:
2478)             Command-line arguments `--overwrite-existing` (True) and
2479)             `--merge-existing` (False).  Controls whether config saving
2480)             and config importing overwrite existing configurations, or
2481)             merge them section-wise instead.
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

2482)         unset_settings:
2483)             Command-line argument `--unset`.  If given together with
2484)             `--config`, unsets the specified settings (in addition to
2485)             any other changes requested).
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

2486)         export_as:
2487)             Command-line argument `--export-as`.  If given together with
2488)             `--export`, selects the format to export the current
2489)             configuration as: JSON ("json", default) or POSIX sh ("sh").
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2490) 
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2491)     """
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2492)     logger = logging.getLogger(PROG_NAME)
2493)     deprecation = logging.getLogger(PROG_NAME + '.deprecation')
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2494)     service_metavar = _msg.TranslatedString(_msg.Label.VAULT_METAVAR_SERVICE)
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2495)     options_in_group: dict[type[click.Option], list[click.Option]] = {}
2496)     params_by_str: dict[str, click.Parameter] = {}
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2497)     for param in ctx.command.params:
2498)         if isinstance(param, click.Option):
2499)             group: type[click.Option]
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

2500)             # Use match/case here once Python 3.9 becomes unsupported.
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2501)             if isinstance(param, PassphraseGenerationOption):
2502)                 group = PassphraseGenerationOption
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

2503)             elif isinstance(param, ConfigurationOption):
2504)                 group = ConfigurationOption
2505)             elif isinstance(param, StorageManagementOption):
2506)                 group = StorageManagementOption
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2507)             elif isinstance(param, LoggingOption):
2508)                 group = LoggingOption
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2509)             elif isinstance(param, CompatibilityOption):
2510)                 group = CompatibilityOption
Marco Ricci Add support for Python 3.9

Marco Ricci authored 3 months ago

2511)             elif isinstance(param, OptionGroupOption):
2512)                 raise AssertionError(  # noqa: DOC501,TRY003,TRY004
2513)                     f'Unknown option group for {param!r}'  # noqa: EM102
2514)                 )
2515)             else:
2516)                 group = click.Option
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2517)             options_in_group.setdefault(group, []).append(param)
2518)         params_by_str[param.human_readable_name] = param
2519)         for name in param.opts + param.secondary_opts:
2520)             params_by_str[name] = param
2521) 
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2522)     @functools.cache
Marco Ricci Fix typing issues in mypy s...

Marco Ricci authored 5 months ago

2523)     def is_param_set(param: click.Parameter) -> bool:
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2524)         return bool(ctx.params.get(param.human_readable_name))
2525) 
Marco Ricci Make the mutually exclusive...

Marco Ricci authored 2 weeks ago

2526)     def option_name(param: click.Parameter | str) -> str:
2527)         # Annoyingly, `param.human_readable_name` contains the *function*
2528)         # parameter name, not the list of option names.  *Those* are
2529)         # stashed in the `.opts` and `.secondary_opts` attributes, which
2530)         # are visible in the `.to_info_dict()` output, but not otherwise
2531)         # documented.
2532)         param = params_by_str[param] if isinstance(param, str) else param
2533)         names = [param.human_readable_name, *param.opts, *param.secondary_opts]
2534)         option_names = [n for n in names if n.startswith('--')]
2535)         return min(option_names, key=len)
2536) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2537)     def check_incompatible_options(
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2538)         param1: click.Parameter | str,
2539)         param2: click.Parameter | str,
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2540)     ) -> None:
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2541)         param1 = params_by_str[param1] if isinstance(param1, str) else param1
2542)         param2 = params_by_str[param2] if isinstance(param2, str) else param2
2543)         if param1 == param2:
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2544)             return
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2545)         if not is_param_set(param1):
2546)             return
2547)         if is_param_set(param2):
Marco Ricci Make the mutually exclusive...

Marco Ricci authored 2 weeks ago

2548)             param1_str = option_name(param1)
2549)             param2_str = option_name(param2)
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2550)             raise click.BadOptionUsage(
Marco Ricci Make the mutually exclusive...

Marco Ricci authored 2 weeks ago

2551)                 param1_str,
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2552)                 str(
2553)                     _msg.TranslatedString(
2554)                         _msg.ErrMsgTemplate.PARAMS_MUTUALLY_EXCLUSIVE,
2555)                         param1=param1_str,
2556)                         param2=param2_str,
2557)                     )
2558)                 ),
Marco Ricci Make the mutually exclusive...

Marco Ricci authored 2 weeks ago

2559)                 ctx=ctx,
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2560)             )
2561)         return
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2562) 
Marco Ricci Force logging calls in erro...

Marco Ricci authored 1 week ago

2563)     def err(msg: Any, /, **kwargs: Any) -> NoReturn:  # noqa: ANN401
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2564)         stacklevel = kwargs.pop('stacklevel', 1)
2565)         stacklevel += 1
Marco Ricci Force logging calls in erro...

Marco Ricci authored 1 week ago

2566)         logger.error(msg, stacklevel=stacklevel, **kwargs)
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2567)         ctx.exit(1)
2568) 
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

2569)     def get_config() -> _types.VaultConfig:
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2570)         try:
2571)             return _load_config()
2572)         except FileNotFoundError:
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

2573)             try:
2574)                 backup_config, exc = _migrate_and_load_old_config()
2575)             except FileNotFoundError:
2576)                 return {'services': {}}
Marco Ricci Make obtaining the compatib...

Marco Ricci authored 3 weeks ago

2577)             old_name = os.path.basename(
2578)                 _config_filename(subsystem='old settings.json')
2579)             )
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

2580)             new_name = os.path.basename(_config_filename(subsystem='vault'))
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2581)             deprecation.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2582)                 _msg.TranslatedString(
2583)                     _msg.WarnMsgTemplate.V01_STYLE_CONFIG,
2584)                     old=old_name,
2585)                     new=new_name,
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

2586)                 ),
2587)             )
2588)             if isinstance(exc, OSError):
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2589)                 logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2590)                     _msg.TranslatedString(
2591)                         _msg.WarnMsgTemplate.FAILED_TO_MIGRATE_CONFIG,
2592)                         path=new_name,
2593)                         error=exc.strerror,
2594)                         filename=exc.filename,
2595)                     ).maybe_without_filename(),
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

2596)                 )
2597)             else:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2598)                 deprecation.info(
2599)                     _msg.TranslatedString(
2600)                         _msg.InfoMsgTemplate.SUCCESSFULLY_MIGRATED,
2601)                         path=new_name,
2602)                     ),
2603)                 )
Marco Ricci Rename the configuration fi...

Marco Ricci authored 3 months ago

2604)             return backup_config
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2605)         except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2606)             err(
2607)                 _msg.TranslatedString(
2608)                     _msg.ErrMsgTemplate.CANNOT_LOAD_VAULT_SETTINGS,
2609)                     error=exc.strerror,
2610)                     filename=exc.filename,
2611)                 ).maybe_without_filename(),
2612)             )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2613)         except Exception as exc:  # noqa: BLE001
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2614)             err(
2615)                 _msg.TranslatedString(
2616)                     _msg.ErrMsgTemplate.CANNOT_LOAD_VAULT_SETTINGS,
2617)                     error=str(exc),
2618)                     filename=None,
2619)                 ).maybe_without_filename(),
2620)                 exc_info=exc,
2621)             )
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2622) 
2623)     def put_config(config: _types.VaultConfig, /) -> None:
2624)         try:
2625)             _save_config(config)
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

2626)         except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2627)             err(
2628)                 _msg.TranslatedString(
2629)                     _msg.ErrMsgTemplate.CANNOT_STORE_VAULT_SETTINGS,
2630)                     error=exc.strerror,
2631)                     filename=exc.filename,
2632)                 ).maybe_without_filename(),
2633)             )
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2634)         except Exception as exc:  # noqa: BLE001
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2635)             err(
2636)                 _msg.TranslatedString(
2637)                     _msg.ErrMsgTemplate.CANNOT_STORE_VAULT_SETTINGS,
2638)                     error=str(exc),
2639)                     filename=None,
2640)                 ).maybe_without_filename(),
2641)                 exc_info=exc,
2642)             )
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2643) 
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

2644)     def get_user_config() -> dict[str, Any]:
2645)         try:
2646)             return _load_user_config()
2647)         except FileNotFoundError:
2648)             return {}
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2649)         except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2650)             err(
2651)                 _msg.TranslatedString(
2652)                     _msg.ErrMsgTemplate.CANNOT_LOAD_USER_CONFIG,
2653)                     error=exc.strerror,
2654)                     filename=exc.filename,
2655)                 ).maybe_without_filename(),
2656)             )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2657)         except Exception as exc:  # noqa: BLE001
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2658)             err(
2659)                 _msg.TranslatedString(
2660)                     _msg.ErrMsgTemplate.CANNOT_LOAD_USER_CONFIG,
2661)                     error=str(exc),
2662)                     filename=None,
2663)                 ).maybe_without_filename(),
2664)                 exc_info=exc,
2665)             )
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

2666) 
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

2667)     configuration: _types.VaultConfig
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2668) 
2669)     check_incompatible_options('--phrase', '--key')
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2670)     for group in (ConfigurationOption, StorageManagementOption):
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2671)         for opt in options_in_group[group]:
2672)             if opt != params_by_str['--config']:
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2673)                 for other_opt in options_in_group[PassphraseGenerationOption]:
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2674)                     check_incompatible_options(opt, other_opt)
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2675) 
Marco Ricci Add prototype command-line...

Marco Ricci authored 6 months ago

2676)     for group in (ConfigurationOption, StorageManagementOption):
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2677)         for opt in options_in_group[group]:
Marco Ricci Rewrite incompatible option...

Marco Ricci authored 3 weeks ago

2678)             for other_opt in options_in_group[ConfigurationOption]:
2679)                 check_incompatible_options(opt, other_opt)
2680)             for other_opt in options_in_group[StorageManagementOption]:
2681)                 check_incompatible_options(opt, other_opt)
Marco Ricci Add an actual derivepassphr...

Marco Ricci authored 2 weeks ago

2682)     sv_or_global_options = options_in_group[PassphraseGenerationOption]
Marco Ricci Correctly model vault globa...

Marco Ricci authored 2 months ago

2683)     for param in sv_or_global_options:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2684)         if is_param_set(param) and not (
2685)             service or is_param_set(params_by_str['--config'])
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2686)         ):
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2687)             err_msg = _msg.TranslatedString(
2688)                 _msg.ErrMsgTemplate.PARAMS_NEEDS_SERVICE_OR_CONFIG,
2689)                 param=param.opts[0],
2690)                 service_metavar=service_metavar,
2691)             )
2692)             raise click.UsageError(str(err_msg))  # noqa: DOC501
Marco Ricci Correctly model vault globa...

Marco Ricci authored 2 months ago

2693)     sv_options = [params_by_str['--notes'], params_by_str['--delete']]
2694)     for param in sv_options:
2695)         if is_param_set(param) and not service:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2696)             err_msg = _msg.TranslatedString(
2697)                 _msg.ErrMsgTemplate.PARAMS_NEEDS_SERVICE,
2698)                 param=param.opts[0],
2699)                 service_metavar=service_metavar,
2700)             )
2701)             raise click.UsageError(str(err_msg))
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2702)     no_sv_options = [
2703)         params_by_str['--delete-globals'],
2704)         params_by_str['--clear'],
2705)         *options_in_group[StorageManagementOption],
2706)     ]
Marco Ricci Fortify the argument parsin...

Marco Ricci authored 6 months ago

2707)     for param in no_sv_options:
2708)         if is_param_set(param) and service:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2709)             err_msg = _msg.TranslatedString(
2710)                 _msg.ErrMsgTemplate.PARAMS_NO_SERVICE,
2711)                 param=param.opts[0],
2712)                 service_metavar=service_metavar,
2713)             )
2714)             raise click.UsageError(str(err_msg))
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2715) 
Marco Ricci Introduce a central user co...

Marco Ricci authored 3 weeks ago

2716)     user_config = get_user_config()
2717) 
Marco Ricci Warn the user upon supplyin...

Marco Ricci authored 2 months ago

2718)     if service == '':  # noqa: PLC1901
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2719)         logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2720)             _msg.TranslatedString(
2721)                 _msg.WarnMsgTemplate.EMPTY_SERVICE_NOT_SUPPORTED,
2722)                 service_metavar=service_metavar,
2723)             )
Marco Ricci Warn the user upon supplyin...

Marco Ricci authored 2 months ago

2724)         )
2725) 
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2726)     if edit_notes:
2727)         assert service is not None
2728)         configuration = get_config()
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2729)         text = DEFAULT_NOTES_TEMPLATE + configuration['services'].get(
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

2730)             service, cast(_types.VaultConfigServicesSettings, {})
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2731)         ).get('notes', '')
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2732)         notes_value = click.edit(text=text)
2733)         if notes_value is not None:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2734)             notes_lines = collections.deque(notes_value.splitlines(True))  # noqa: FBT003
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2735)             while notes_lines:
2736)                 line = notes_lines.popleft()
2737)                 if line.startswith(DEFAULT_NOTES_MARKER):
2738)                     notes_value = ''.join(notes_lines)
2739)                     break
2740)             else:
2741)                 if not notes_value.strip():
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2742)                     err(
2743)                         _msg.TranslatedString(
2744)                             _msg.ErrMsgTemplate.USER_ABORTED_EDIT
2745)                         )
2746)                     )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2747)             configuration['services'].setdefault(service, {})['notes'] = (
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2748)                 notes_value.strip('\n')
2749)             )
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2750)             put_config(configuration)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2751)     elif delete_service_settings:
2752)         assert service is not None
2753)         configuration = get_config()
2754)         if service in configuration['services']:
2755)             del configuration['services'][service]
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2756)             put_config(configuration)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2757)     elif delete_globals:
2758)         configuration = get_config()
2759)         if 'global' in configuration:
2760)             del configuration['global']
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2761)             put_config(configuration)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2762)     elif clear_all_settings:
Marco Ricci Use better error message ha...

Marco Ricci authored 4 months ago

2763)         put_config({'services': {}})
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2764)     elif import_settings:
2765)         try:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2766)             # TODO(the-13th-letter): keep track of auto-close; try
2767)             # os.dup if feasible
Marco Ricci Document handling of file o...

Marco Ricci authored 3 weeks ago

2768)             infile = cast(
2769)                 TextIO,
2770)                 (
2771)                     import_settings
2772)                     if hasattr(import_settings, 'close')
2773)                     else click.open_file(os.fspath(import_settings), 'rt')
2774)                 ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2775)             )
Marco Ricci Document handling of file o...

Marco Ricci authored 3 weeks ago

2776)             # Don't specifically catch TypeError or ValueError here if
2777)             # the passed-in fileobj is not a readable text stream.  This
2778)             # will never happen on the command-line (thanks to `click`),
2779)             # and for programmatic use, our caller may want accurate
2780)             # error information.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2781)             with infile:
2782)                 maybe_config = json.load(infile)
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2783)         except json.JSONDecodeError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2784)             err(
2785)                 _msg.TranslatedString(
2786)                     _msg.ErrMsgTemplate.CANNOT_DECODEIMPORT_VAULT_SETTINGS,
2787)                     error=exc,
2788)                 )
2789)             )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2790)         except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2791)             err(
2792)                 _msg.TranslatedString(
2793)                     _msg.ErrMsgTemplate.CANNOT_IMPORT_VAULT_SETTINGS,
2794)                     error=exc.strerror,
2795)                     filename=exc.filename,
2796)                 ).maybe_without_filename()
2797)             )
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

2798)         cleaned = _types.clean_up_falsy_vault_config_values(maybe_config)
2799)         if not _types.is_vault_config(maybe_config):
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2800)             err(
2801)                 _msg.TranslatedString(
2802)                     _msg.ErrMsgTemplate.CANNOT_IMPORT_VAULT_SETTINGS,
2803)                     error=_msg.TranslatedString(
2804)                         _msg.ErrMsgTemplate.INVALID_VAULT_CONFIG,
2805)                         config=maybe_config,
2806)                     ),
2807)                     filename=None,
2808)                 ).maybe_without_filename()
2809)             )
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

2810)         assert cleaned is not None
2811)         for step in cleaned:
2812)             # These are never fatal errors, because the semantics of
2813)             # vault upon encountering these settings are ill-specified,
2814)             # but not ill-defined.
2815)             if step.action == 'replace':
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2816)                 logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2817)                     _msg.TranslatedString(
2818)                         _msg.WarnMsgTemplate.STEP_REPLACE_INVALID_VALUE,
2819)                         old=json.dumps(step.old_value),
2820)                         path=_types.json_path(step.path),
2821)                         new=json.dumps(step.new_value),
2822)                     ),
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

2823)                 )
2824)             else:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2825)                 logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2826)                     _msg.TranslatedString(
2827)                         _msg.WarnMsgTemplate.STEP_REMOVE_INEFFECTIVE_VALUE,
2828)                         path=_types.json_path(step.path),
2829)                         old=json.dumps(step.old_value),
2830)                     ),
Marco Ricci Signal and list falsy value...

Marco Ricci authored 3 months ago

2831)                 )
Marco Ricci Warn the user upon supplyin...

Marco Ricci authored 2 months ago

2832)         if '' in maybe_config['services']:
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2833)             logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2834)                 _msg.TranslatedString(
2835)                     _msg.WarnMsgTemplate.EMPTY_SERVICE_SETTINGS_INACCESSIBLE,
2836)                     service_metavar=service_metavar,
2837)                     PROG_NAME=PROG_NAME,
Marco Ricci Use the logging system to e...

Marco Ricci authored 1 month ago

2838)                 ),
Marco Ricci Warn the user upon supplyin...

Marco Ricci authored 2 months ago

2839)             )
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

2840)         try:
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

2841)             _check_for_misleading_passphrase(
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

2842)                 ('global',),
2843)                 cast(dict[str, Any], maybe_config.get('global', {})),
2844)                 main_config=user_config,
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

2845)             )
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

2846)             for key, value in maybe_config['services'].items():
2847)                 _check_for_misleading_passphrase(
2848)                     ('services', key),
2849)                     cast(dict[str, Any], value),
2850)                     main_config=user_config,
2851)                 )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2852)         except AssertionError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2853)             err(
2854)                 _msg.TranslatedString(
2855)                     _msg.ErrMsgTemplate.INVALID_USER_CONFIG,
2856)                     error=exc,
2857)                     filename=None,
2858)                 ).maybe_without_filename(),
2859)             )
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

2860)         global_obj = maybe_config.get('global', {})
2861)         has_key = _types.js_truthiness(global_obj.get('key'))
2862)         has_phrase = _types.js_truthiness(global_obj.get('phrase'))
2863)         if has_key and has_phrase:
2864)             logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2865)                 _msg.TranslatedString(
2866)                     _msg.WarnMsgTemplate.GLOBAL_PASSPHRASE_INEFFECTIVE,
2867)                 )
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

2868)             )
2869)         for service_name, service_obj in maybe_config['services'].items():
2870)             has_key = _types.js_truthiness(
2871)                 service_obj.get('key')
2872)             ) or _types.js_truthiness(global_obj.get('key'))
2873)             has_phrase = _types.js_truthiness(
2874)                 service_obj.get('phrase')
2875)             ) or _types.js_truthiness(global_obj.get('phrase'))
2876)             if has_key and has_phrase:
2877)                 logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2878)                     _msg.TranslatedString(
2879)                         _msg.WarnMsgTemplate.SERVICE_PASSPHRASE_INEFFECTIVE,
2880)                         service=json.dumps(service_name),
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

2881)                     ),
2882)                 )
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

2883)         if overwrite_config:
2884)             put_config(maybe_config)
2885)         else:
2886)             configuration = get_config()
2887)             merged_config: collections.ChainMap[str, Any] = (
2888)                 collections.ChainMap(
2889)                     {
2890)                         'services': collections.ChainMap(
2891)                             maybe_config['services'],
2892)                             configuration['services'],
2893)                         ),
2894)                     },
2895)                     {'global': maybe_config['global']}
2896)                     if 'global' in maybe_config
2897)                     else {},
2898)                     {'global': configuration['global']}
2899)                     if 'global' in configuration
2900)                     else {},
2901)                 )
2902)             )
2903)             new_config: Any = {
2904)                 k: dict(v) if isinstance(v, collections.ChainMap) else v
2905)                 for k, v in sorted(merged_config.items())
2906)             }
2907)             assert _types.is_vault_config(new_config)
2908)             put_config(new_config)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2909)     elif export_settings:
2910)         configuration = get_config()
2911)         try:
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 4 months ago

2912)             # TODO(the-13th-letter): keep track of auto-close; try
2913)             # os.dup if feasible
Marco Ricci Document handling of file o...

Marco Ricci authored 3 weeks ago

2914)             outfile = cast(
2915)                 TextIO,
2916)                 (
2917)                     export_settings
2918)                     if hasattr(export_settings, 'close')
2919)                     else click.open_file(os.fspath(export_settings), 'wt')
2920)                 ),
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2921)             )
Marco Ricci Document handling of file o...

Marco Ricci authored 3 weeks ago

2922)             # Don't specifically catch TypeError or ValueError here if
2923)             # the passed-in fileobj is not a writable text stream.  This
2924)             # will never happen on the command-line (thanks to `click`),
2925)             # and for programmatic use, our caller may want accurate
2926)             # error information.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2927)             with outfile:
Marco Ricci Support exporting the `vaul...

Marco Ricci authored 2 weeks ago

2928)                 if export_as == 'sh':
2929)                     this_ctx = ctx
2930)                     prog_name_pieces = collections.deque([
2931)                         this_ctx.info_name or 'vault',
2932)                     ])
2933)                     while (
2934)                         this_ctx.parent is not None
2935)                         and this_ctx.parent.info_name is not None
2936)                     ):
2937)                         prog_name_pieces.appendleft(this_ctx.parent.info_name)
2938)                         this_ctx = this_ctx.parent
2939)                     _print_config_as_sh_script(
2940)                         configuration,
2941)                         outfile=outfile,
2942)                         prog_name_list=prog_name_pieces,
2943)                     )
2944)                 else:
2945)                     json.dump(configuration, outfile)
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

2946)         except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2947)             err(
2948)                 _msg.TranslatedString(
2949)                     _msg.ErrMsgTemplate.CANNOT_EXPORT_VAULT_SETTINGS,
2950)                     error=exc.strerror,
2951)                     filename=exc.filename,
2952)                 ).maybe_without_filename(),
2953)             )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2954)     else:
2955)         configuration = get_config()
2956)         # This block could be type checked more stringently, but this
2957)         # would probably involve a lot of code repetition.  Since we
2958)         # have a type guarding function anyway, assert that we didn't
2959)         # make any mistakes at the end instead.
2960)         global_keys = {'key', 'phrase'}
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2961)         service_keys = {
2962)             'key',
2963)             'phrase',
2964)             'length',
2965)             'repeat',
2966)             'lower',
2967)             'upper',
2968)             'number',
2969)             'space',
2970)             'dash',
2971)             'symbol',
2972)         }
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2973)         settings: collections.ChainMap[str, Any] = collections.ChainMap(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2974)             {
2975)                 k: v
2976)                 for k, v in locals().items()
2977)                 if k in service_keys and v is not None
2978)             },
2979)             cast(
2980)                 dict[str, Any],
2981)                 configuration['services'].get(service or '', {}),
2982)             ),
2983)             cast(dict[str, Any], configuration.get('global', {})),
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2984)         )
2985)         if use_key:
2986)             try:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

2987)                 key = base64.standard_b64encode(_select_ssh_key()).decode(
2988)                     'ASCII'
2989)                 )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

2990)             except IndexError:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2991)                 err(
2992)                     _msg.TranslatedString(
2993)                         _msg.ErrMsgTemplate.USER_ABORTED_SSH_KEY_SELECTION
2994)                     ),
2995)                 )
Marco Ricci Document and handle other e...

Marco Ricci authored 4 months ago

2996)             except KeyError:
Marco Ricci Fail gracefully if UNIX dom...

Marco Ricci authored 3 months ago

2997)                 err(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

2998)                     _msg.TranslatedString(
2999)                         _msg.ErrMsgTemplate.NO_SSH_AGENT_FOUND
3000)                     ),
3001)                 )
3002)             except LookupError:
3003)                 err(
3004)                     _msg.TranslatedString(
3005)                         _msg.ErrMsgTemplate.NO_SUITABLE_SSH_KEYS,
3006)                         PROG_NAME=PROG_NAME,
3007)                     )
Marco Ricci Fail gracefully if UNIX dom...

Marco Ricci authored 3 months ago

3008)                 )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3009)             except NotImplementedError:
3010)                 err(_msg.TranslatedString(_msg.ErrMsgTemplate.NO_AF_UNIX))
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

3011)             except OSError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3012)                 err(
3013)                     _msg.TranslatedString(
3014)                         _msg.ErrMsgTemplate.CANNOT_CONNECT_TO_AGENT,
3015)                         error=exc.strerror,
3016)                         filename=exc.filename,
3017)                     ).maybe_without_filename(),
3018)                 )
3019)             except ssh_agent.SSHAgentFailedError as exc:
3020)                 err(
3021)                     _msg.TranslatedString(
3022)                         _msg.ErrMsgTemplate.AGENT_REFUSED_LIST_KEYS
3023)                     ),
3024)                     exc_info=exc,
3025)                 )
3026)             except RuntimeError as exc:
3027)                 err(
3028)                     _msg.TranslatedString(
3029)                         _msg.ErrMsgTemplate.CANNOT_UNDERSTAND_AGENT
3030)                     ),
3031)                     exc_info=exc,
3032)                 )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3033)         elif use_phrase:
3034)             maybe_phrase = _prompt_for_passphrase()
3035)             if not maybe_phrase:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3036)                 err(
3037)                     _msg.TranslatedString(
3038)                         _msg.ErrMsgTemplate.USER_ABORTED_PASSPHRASE
3039)                     )
3040)                 )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3041)             else:
3042)                 phrase = maybe_phrase
3043)         if store_config_only:
3044)             view: collections.ChainMap[str, Any]
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3045)             view = (
3046)                 collections.ChainMap(*settings.maps[:2])
3047)                 if service
Marco Ricci Fix missing consideration o...

Marco Ricci authored 2 months ago

3048)                 else collections.ChainMap(settings.maps[0], settings.maps[2])
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3049)             )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3050)             if use_key:
3051)                 view['key'] = key
3052)             elif use_phrase:
Marco Ricci Fix missing consideration o...

Marco Ricci authored 2 months ago

3053)                 view['phrase'] = phrase
3054)                 settings_type = 'service' if service else 'global'
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

3055)                 try:
3056)                     _check_for_misleading_passphrase(
3057)                         ('services', service) if service else ('global',),
3058)                         {'phrase': phrase},
3059)                         main_config=user_config,
3060)                     )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

3061)                 except AssertionError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3062)                     err(
3063)                         _msg.TranslatedString(
3064)                             _msg.ErrMsgTemplate.INVALID_USER_CONFIG,
3065)                             error=exc,
3066)                             filename=None,
3067)                         ).maybe_without_filename(),
3068)                     )
Marco Ricci Fix missing consideration o...

Marco Ricci authored 2 months ago

3069)                 if 'key' in settings:
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

3070)                     if service:
3071)                         logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3072)                             _msg.TranslatedString(
3073)                                 _msg.WarnMsgTemplate.SERVICE_PASSPHRASE_INEFFECTIVE,
3074)                                 service=json.dumps(service),
3075)                             )
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

3076)                         )
3077)                     else:
3078)                         logger.warning(
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3079)                             _msg.TranslatedString(
3080)                                 _msg.WarnMsgTemplate.GLOBAL_PASSPHRASE_INEFFECTIVE
3081)                             )
Marco Ricci Fix empty key handling in `...

Marco Ricci authored 3 weeks ago

3082)                         )
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

3083)             if not view.maps[0] and not unset_settings:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 5 months ago

3084)                 settings_type = 'service' if service else 'global'
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3085)                 err_msg = _msg.TranslatedString(
3086)                     _msg.ErrMsgTemplate.CANNOT_UPDATE_SETTINGS_NO_SETTINGS,
3087)                     settings_type=settings_type,
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3088)                 )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3089)                 raise click.UsageError(str(err_msg))
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

3090)             for setting in unset_settings:
3091)                 if setting in view.maps[0]:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3092)                     err_msg = _msg.TranslatedString(
3093)                         _msg.ErrMsgTemplate.SET_AND_UNSET_SAME_SETTING,
3094)                         setting=setting,
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

3095)                     )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3096)                     raise click.UsageError(str(err_msg))
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

3097)             subtree: dict[str, Any] = (
3098)                 configuration['services'].setdefault(service, {})  # type: ignore[assignment]
3099)                 if service
3100)                 else configuration.setdefault('global', {})
3101)             )
3102)             if overwrite_config:
3103)                 subtree.clear()
Marco Ricci Allow unsetting settings wh...

Marco Ricci authored 2 weeks ago

3104)             else:
3105)                 for setting in unset_settings:
3106)                     subtree.pop(setting, None)
Marco Ricci Allow the user to overwrite...

Marco Ricci authored 4 weeks ago

3107)             subtree.update(view)
Marco Ricci Consolidate `types` submodu...

Marco Ricci authored 5 months ago

3108)             assert _types.is_vault_config(
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3109)                 configuration
Marco Ricci Fix error message capitaliz...

Marco Ricci authored 4 months ago

3110)             ), f'Invalid vault configuration: {configuration!r}'
Marco Ricci Fix error bubbling in outda...

Marco Ricci authored 4 months ago

3111)             put_config(configuration)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3112)         else:
3113)             if not service:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3114)                 err_msg = _msg.TranslatedString(
3115)                     _msg.ErrMsgTemplate.SERVICE_REQUIRED,
3116)                     service_metavar=_msg.TranslatedString(
3117)                         _msg.Label.VAULT_METAVAR_SERVICE
3118)                     ),
3119)                 )
3120)                 raise click.UsageError(str(err_msg))
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3121)             kwargs: dict[str, Any] = {
3122)                 k: v
3123)                 for k, v in settings.items()
3124)                 if k in service_keys and v is not None
3125)             }
3126) 
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

3127)             if use_phrase:
Marco Ricci Turn Unicode normalization...

Marco Ricci authored 3 weeks ago

3128)                 try:
3129)                     _check_for_misleading_passphrase(
3130)                         _ORIGIN.INTERACTIVE,
3131)                         {'phrase': phrase},
3132)                         main_config=user_config,
3133)                     )
Marco Ricci Add small fixes to changelo...

Marco Ricci authored 2 weeks ago

3134)                 except AssertionError as exc:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3135)                     err(
3136)                         _msg.TranslatedString(
3137)                             _msg.ErrMsgTemplate.INVALID_USER_CONFIG,
3138)                             error=exc,
3139)                             filename=None,
3140)                         ).maybe_without_filename(),
3141)                     )
Marco Ricci Allow all textual strings,...

Marco Ricci authored 4 months ago

3142) 
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3143)             # If either --key or --phrase are given, use that setting.
3144)             # Otherwise, if both key and phrase are set in the config,
Marco Ricci Align behavior with vault c...

Marco Ricci authored 3 months ago

3145)             # use the key.  Otherwise, if only one of key and phrase is
3146)             # set in the config, use that one.  In all these above
3147)             # cases, set the phrase via vault.Vault.phrase_from_key if
3148)             # a key is given.  Finally, if nothing is set, error out.
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3149)             if use_key or use_phrase:
Marco Ricci Fix formatting, some covera...

Marco Ricci authored 2 weeks ago

3150)                 kwargs['phrase'] = (
3151)                     _key_to_phrase(key, error_callback=err)
3152)                     if use_key
3153)                     else phrase
3154)                 )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3155)             elif kwargs.get('key'):
Marco Ricci Hoist and add tests for int...

Marco Ricci authored 3 weeks ago

3156)                 kwargs['phrase'] = _key_to_phrase(
3157)                     kwargs['key'], error_callback=err
3158)                 )
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3159)             elif kwargs.get('phrase'):
3160)                 pass
3161)             else:
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3162)                 err_msg = _msg.TranslatedString(
3163)                     _msg.ErrMsgTemplate.NO_KEY_OR_PHRASE
Marco Ricci Reformat everything with ruff

Marco Ricci authored 5 months ago

3164)                 )
Marco Ricci Update the CLI to use the t...

Marco Ricci authored 1 week ago

3165)                 raise click.UsageError(str(err_msg))
Marco Ricci Avoid crashing when overrid...

Marco Ricci authored 5 months ago

3166)             kwargs.pop('key', '')
Marco Ricci Move `sequin` and `ssh_agen...

Marco Ricci authored 5 months ago

3167)             result = vault.Vault(**kwargs).generate(service)
Marco Ricci Add finished command-line i...

Marco Ricci authored 6 months ago

3168)             click.echo(result.decode('ASCII'))