Skip to content

Commit b679ad2

Browse files
Clean up and refactor code
* Refactor admin and upload api command handling * Make `cli.py` executable * Refactor, extract and reorganise methods Improve readability * Fix logging level Eliminate `thread_utils` * Add deletion confirmation for `sync` command
1 parent bc73c89 commit b679ad2

24 files changed

+766
-635
lines changed

cloudinary_cli/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import cloudinary
2-
31
__version__ = "0.4.2"
42

5-
cloudinary.USER_PLATFORM = "CloudinaryCLI/{}".format(__version__)
3+
import cloudinary
4+
5+
cloudinary.USER_PLATFORM = f"CloudinaryCLI/{__version__}"

cloudinary_cli/cli.py

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,66 @@
11
#!/usr/bin/env python3
2-
import json
3-
import os
2+
43
import click
4+
import click_log
55
import cloudinary
6+
67
import cloudinary_cli.core
78
import cloudinary_cli.modules
89
import cloudinary_cli.samples
9-
import click_log
10-
11-
from cloudinary_cli.defaults import CLOUDINARY_CLI_CONFIG_FILE
12-
from cloudinary_cli.utils import logger, initialize
10+
from cloudinary_cli.defaults import logger
11+
from cloudinary_cli.utils.config_utils import initialize, load_config, refresh_cloudinary_config
12+
from cloudinary_cli.utils.utils import log_exception
1313

1414
CONTEXT_SETTINGS = dict(max_content_width=click.get_terminal_size()[0], terminal_width=click.get_terminal_size()[0])
1515

1616

1717
@click.group(context_settings=CONTEXT_SETTINGS)
18-
@click.option("-c", "--config", help="""Tell the CLI which account to run the command on by specifying an account environment variable.
19-
""")
20-
@click.option("-C", "--config_saved", help="""Tell the CLI which account to run the command on by specifying a saved configuration - see `config` command.""")
18+
@click.option("-c", "--config",
19+
help="""Tell the CLI which account to run the command on by specifying an account environment variable."""
20+
)
21+
@click.option("-C", "--config_saved",
22+
help="""Tell the CLI which account to run the command on by specifying a saved configuration - see
23+
`config` command.""")
2124
@click_log.simple_verbosity_option(logger)
2225
def cli(config, config_saved):
2326
if config:
24-
os.environ.update(dict(CLOUDINARY_URL=config))
27+
refresh_cloudinary_config(config)
2528
elif config_saved:
26-
with open(CLOUDINARY_CLI_CONFIG_FILE) as f:
27-
os.environ.update(dict(CLOUDINARY_URL=json.loads(f.read())[config_saved]))
28-
cloudinary.reset_config()
29+
config = load_config()
30+
if config_saved not in config:
31+
raise Exception(f"Config {config_saved} does not exist")
32+
33+
refresh_cloudinary_config(config[config_saved])
34+
2935
if cloudinary.config().cloud_name is None:
3036
logger.warning("No Cloudinary configuration found.")
31-
pass
3237

38+
return 0
3339

34-
cloudinary_cli.core.import_commands(cli)
35-
cloudinary_cli.modules.import_commands(cli)
36-
cloudinary_cli.samples.import_commands(cli)
40+
41+
def import_commands(*command_modules):
42+
for command_module in command_modules:
43+
for command in command_module:
44+
cli.add_command(command)
45+
46+
47+
import_commands(
48+
cloudinary_cli.core.commands,
49+
cloudinary_cli.modules.commands,
50+
cloudinary_cli.samples.commands,
51+
)
3752

3853

3954
def main():
4055
initialize()
4156
try:
42-
cli()
57+
exit_status = cli()
4358
except Exception as e:
44-
logger.error(str(e))
59+
log_exception(e, "Command execution failed")
60+
exit_status = 1
61+
62+
return exit_status
63+
64+
65+
if __name__ == "__main__":
66+
main()

cloudinary_cli/core/__init__.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import click
2-
import cloudinary.uploader
32

43
from cloudinary_cli.core.admin import admin
54
from cloudinary_cli.core.config import config
6-
from cloudinary_cli.core.overrides import resolve_command, upload
75
from cloudinary_cli.core.search import search
86
from cloudinary_cli.core.uploader import uploader
97
from cloudinary_cli.core.utils import url
8+
from cloudinary_cli.core.overrides import resolve_command
109

1110
setattr(click.MultiCommand, "resolve_command", resolve_command)
12-
cloudinary.uploader.upload = upload
1311

14-
15-
def import_commands(cli):
16-
17-
cli.add_command(config)
18-
cli.add_command(search)
19-
cli.add_command(admin)
20-
cli.add_command(uploader)
21-
cli.add_command(url)
12+
commands = [
13+
config,
14+
search,
15+
admin,
16+
uploader,
17+
url,
18+
]

cloudinary_cli/core/admin.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
from webbrowser import open as open_url
2-
31
from click import command, argument, option
42
from cloudinary import api
53

6-
from cloudinary_cli.utils import get_help, parse_args_kwargs, parse_option_value, log_json, write_out, logger
4+
from cloudinary_cli.utils.api_utils import handle_api_command
75

86

97
@command("admin",
@@ -25,24 +23,7 @@
2523
@option("--save", nargs=1, help="Save output to a file.")
2624
@option("-d", "--doc", is_flag=True, help="Open the Admin API reference in a browser.")
2725
def admin(params, optional_parameter, optional_parameter_parsed, ls, save, doc):
28-
if doc:
29-
open_url("https://cloudinary.com/documentation/admin_api")
30-
return
31-
if ls or len(params) < 1:
32-
logger.info(get_help(api))
33-
return
34-
try:
35-
func = api.__dict__[params[0]]
36-
if not callable(func):
37-
raise Exception("{} is not callable".format(func))
38-
except Exception:
39-
raise Exception("Method {} does not exist in the Admin API.".format(params[0]))
40-
parameters, options = parse_args_kwargs(func, params[1:]) if len(params) > 1 else ([], {})
41-
res = func(*parameters, **{
42-
**options,
43-
**{k: v for k, v in optional_parameter},
44-
**{k: parse_option_value(v) for k, v in optional_parameter_parsed},
45-
})
46-
log_json(res)
47-
if save:
48-
write_out(res, save)
26+
return handle_api_command(params, optional_parameter, optional_parameter_parsed, ls, save, doc,
27+
doc_url="https://cloudinary.com/documentation/admin_api",
28+
api_instance=api,
29+
api_name="admin")

cloudinary_cli/core/config.py

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,40 @@
1-
import json
2-
31
import cloudinary
4-
from click import command, option
5-
from cloudinary import api
2+
from click import command, option, echo
63

7-
from cloudinary_cli.utils import CLOUDINARY_CLI_CONFIG_FILE, refresh_config, logger
4+
from cloudinary_cli.defaults import logger
5+
from cloudinary_cli.utils.config_utils import load_config, verify_cloudinary_url, update_config, remove_config_keys
86

97

108
@command("config", help="Display the current configuration, and manage additional configurations.")
119
@option("-n", "--new", help="""\b Create and name a configuration from a Cloudinary account environment variable.
1210
e.g. cld config -n <NAME> <CLOUDINARY_URL>""", nargs=2)
1311
@option("-ls", "--ls", help="List all saved configurations.", is_flag=True)
1412
@option("-rm", "--rm", help="Delete a specified configuration.", nargs=1)
15-
@option("-url", "--from_url", help="Create a configuration from a Cloudinary account environment variable. The configuration name is the cloud name.", nargs=1)
13+
@option("-url", "--from_url",
14+
help="Create a configuration from a Cloudinary account environment variable. "
15+
"The configuration name is the cloud name.",
16+
nargs=1)
1617
def config(new, ls, rm, from_url):
17-
if not (new or ls or rm or from_url):
18-
logger.info('\n'.join(["{}:\t{}".format(k, v if k != "api_secret"
19-
else "***************{}".format(v[-4:]))
20-
for k, v in cloudinary.config().__dict__.items()]))
21-
return
18+
if new or from_url:
19+
config_name, cloudinary_url = new or [None, from_url]
2220

23-
with open(CLOUDINARY_CLI_CONFIG_FILE, "r+") as f:
24-
fi = f.read()
25-
cfg = json.loads(fi) if fi != "" else {}
26-
f.close()
27-
if new:
28-
try:
29-
refresh_config(new[1])
30-
cfg[new[0]] = new[1]
31-
api.ping()
32-
with open(CLOUDINARY_CLI_CONFIG_FILE, "w") as f:
33-
f.write(json.dumps(cfg))
34-
f.close()
35-
logger.info("Config '{}' saved!".format(new[0]))
36-
except Exception as e:
37-
logger.error("Invalid Cloudinary URL: {}".format(new[1]))
38-
raise e
39-
return
40-
if ls:
41-
logger.info("\n".join(cfg.keys()))
42-
if rm:
43-
if rm not in cfg.keys():
44-
logger.warn("Configuration '{}' not found.".format(rm))
21+
if not verify_cloudinary_url(cloudinary_url):
4522
return
46-
del cfg[rm]
47-
open(CLOUDINARY_CLI_CONFIG_FILE, "w").write(json.dumps(cfg))
48-
logger.info("Configuration '{}' deleted".format(rm))
49-
return
50-
if from_url:
51-
if "CLOUDINARY_URL=" in from_url:
52-
from_url = from_url[15:]
53-
try:
54-
refresh_config(from_url)
55-
cfg[cloudinary.config().cloud_name] = from_url
56-
api.ping()
57-
with open(CLOUDINARY_CLI_CONFIG_FILE, "w") as f:
58-
f.write(json.dumps(cfg))
59-
f.close()
60-
logger.info("Config '{}' saved!".format(cloudinary.config().cloud_name))
61-
logger.info("Example usage: cld -C {} <command>".format(cloudinary.config().cloud_name))
62-
except Exception as e:
63-
logger.error("Invalid Cloudinary URL: {}".format(from_url))
64-
raise e
23+
24+
config_name = config_name or cloudinary.config().cloud_name
25+
26+
update_config({config_name: cloudinary_url})
27+
28+
logger.info("Config '{}' saved!".format(config_name))
29+
logger.info("Example usage: cld -C {} <command>".format(config_name))
30+
elif rm:
31+
if remove_config_keys(rm):
32+
logger.warn(f"Configuration '{rm}' not found.")
33+
else:
34+
logger.info(f"Configuration '{rm}' deleted")
35+
elif ls:
36+
echo("\n".join(load_config().keys()))
37+
else:
38+
obfuscated_config = {k: v if k != "api_secret" else "***************{}".format(v[-4:])
39+
for k, v in cloudinary.config().__dict__.items()}
40+
echo('\n'.join(["{}:\t{}".format(k, v) for k, v in obfuscated_config.items()]))

0 commit comments

Comments
 (0)