From 521bf2a4a41f6830d561dc1993275ca152428596 Mon Sep 17 00:00:00 2001 From: David Barnett Date: Sat, 5 Oct 2024 11:57:48 -0600 Subject: [PATCH] Support oauth (and cache) files in $GCALCLI_CONFIG dir (#806) --- ChangeLog | 3 ++ gcalcli/argparsers.py | 9 +++-- gcalcli/cli.py | 4 ++- gcalcli/env.py | 26 +++++++++----- gcalcli/gcal.py | 35 ++++++++++++------- gcalcli/utils.py | 2 +- .../test-02-test_prints_correct_help.snap | 5 ++- 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4a0b383..d3d139b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +v4.5.2 + * Support oauth (and cache) files in $GCALCLI_CONFIG dir + v4.5.1 * Fix gcalcli failing to run on python 3.10 if config file is present * Fix `config edit` when missing config dir blowing up with FileNotFoundError diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index c801117..3e84cc0 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -372,11 +372,10 @@ class RawDescArgDefaultsHelpFormatter( %(prog)s supports a few other configuration mechanisms in addition to the command-line arguments listed below. - $GCALCLI_CONFIG={config_dir} + $GCALCLI_CONFIG={config_path} Path to user config directory or file. - Note: this path is also used to determine fallback paths to check - for cache/oauth files to be migrated into their proper data dir - paths. + Note: you can place an 'oauth' file in this config directory to + support using different accounts per config. {config_file} A toml config file where some general-purpose settings can be @@ -409,7 +408,7 @@ def get_argument_parser(): parser = argparse.ArgumentParser( description=DESCRIPTION.format( - config_dir=config_path, + config_path=config_path, config_file=utils.shorten_path(env.config_file()), rc_paths=', '.join(str(p) for p in rc_paths), ), diff --git a/gcalcli/cli.py b/gcalcli/cli.py index d1db09b..5f7ad1d 100755 --- a/gcalcli/cli.py +++ b/gcalcli/cli.py @@ -360,7 +360,9 @@ def main(): print(json.dumps(schema, indent=2)) elif parsed_args.subcommand == 'reset-cache': deleted_something = False - for cache_filepath in env.data_file_paths('cache'): + for (cache_filepath, _) in env.data_file_paths( + 'cache', parsed_args.config_folder + ): if cache_filepath.exists(): printer.msg( f'Deleting cache file from {cache_filepath}...\n' diff --git a/gcalcli/env.py b/gcalcli/env.py index 79ad4f5..e339a45 100644 --- a/gcalcli/env.py +++ b/gcalcli/env.py @@ -1,6 +1,6 @@ import os import pathlib -from typing import Optional +from typing import Optional, Tuple import platformdirs @@ -14,18 +14,26 @@ def default_data_dir() -> pathlib.Path: def data_file_paths( name: str, config_dir: Optional[pathlib.Path] = None, - ) -> list[pathlib.Path]: + ) -> list[Tuple[pathlib.Path, int]]: """Return all paths actively used for the given data file name. - The paths are returned with the preferred path first followed by any other - detected legacy paths in order of decreasing precedence. + The paths are returned as tuples in order of decreasing precedence like: + [(CONFIG/name, 1), (DATA_DIR/name, 0), (~/.gcalcli_{name}, -1)] + with the DATA_DIR path always present and others only present if the file + exists. """ - paths = [default_data_dir().joinpath(name)] - legacy_path = (config_dir.joinpath(name) - if config_dir - else pathlib.Path(f'~/.gcalcli_{name}').expanduser()) + paths = [] + # Path in config dir takes precedence, if any. + if config_dir: + path_in_config = config_dir.joinpath(name) + if path_in_config.exists(): + paths.append((path_in_config, 1)) + # Standard data path comes next. + paths.append((default_data_dir().joinpath(name), 0)) + # Lastly, fall back to legacy path if it exists and there's no config dir. + legacy_path = pathlib.Path(f'~/.gcalcli_{name}').expanduser() if legacy_path.exists(): - paths.append(legacy_path) + paths.append((legacy_path, -1)) return paths diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index d29589d..a777023 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -142,19 +142,28 @@ def _retry_with_backoff(self, method: googleapiclient.http.HttpRequest): @functools.cache def data_file_path(self, name: str) -> pathlib.Path: - paths = env.data_file_paths(name) - primary_path = paths.pop(0) - if not primary_path.exists(): - for alt_path in paths: - if not alt_path.exists(): - continue - self.printer.msg( - f'Moving {name} file from legacy path {alt_path} to ' - f'{primary_path}...\n' - ) - primary_path.parent.mkdir(parents=True, exist_ok=True) - shutil.move(alt_path, primary_path) - break + paths = env.data_file_paths(name, self.options.get('config_folder')) + primary_path = None + legacy_path = None + for (path, category) in paths: + if path.exists(): + if category >= 0: + return path + else: + legacy_path = path + elif category == 0: + primary_path = path + assert primary_path is not None + + # No non-legacy config file found. Return primary_path, and move legacy + # file to that path if any. + if legacy_path: + self.printer.msg( + f'Moving {name} file from legacy path {legacy_path} to ' + f'{primary_path}...\n' + ) + primary_path.parent.mkdir(parents=True, exist_ok=True) + shutil.move(legacy_path, primary_path) return primary_path diff --git a/gcalcli/utils.py b/gcalcli/utils.py index 04ccab9..2d83c56 100644 --- a/gcalcli/utils.py +++ b/gcalcli/utils.py @@ -242,7 +242,7 @@ def shorten_path(path: pathlib.Path) -> pathlib.Path: def inspect_auth() -> dict[str, Any]: auth_data: dict[str, Any] = OrderedDict() auth_path = None - for path in env.data_file_paths('oauth'): + for (path, _) in env.data_file_paths('oauth', env.config_dir()): if path.exists(): auth_path = path auth_data['path'] = shorten_path(path) diff --git a/tests/cli/__snapshot__/test-02-test_prints_correct_help.snap b/tests/cli/__snapshot__/test-02-test_prints_correct_help.snap index a34cd77..fe24db1 100644 --- a/tests/cli/__snapshot__/test-02-test_prints_correct_help.snap +++ b/tests/cli/__snapshot__/test-02-test_prints_correct_help.snap @@ -17,9 +17,8 @@ configuration: $GCALCLI_CONFIG=/some/gcalcli/config Path to user config directory or file. - Note: this path is also used to determine fallback paths to check - for cache/oauth files to be migrated into their proper data dir - paths. + Note: you can place an 'oauth' file in this config directory to + support using different accounts per config. /some/gcalcli/config/config.toml A toml config file where some general-purpose settings can be