Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions cloudinary_cli/cli_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
CONTEXT_SETTINGS = dict(max_content_width=shutil.get_terminal_size()[0], terminal_width=shutil.get_terminal_size()[0])


@click.group(context_settings=CONTEXT_SETTINGS)
@click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True)
@click.help_option()
@click.version_option(cli_version, prog_name="Cloudinary CLI",
message=f"%(prog)s, version %(version)s\n"
Expand All @@ -24,10 +24,11 @@
help="""Tell the CLI which account to run the command on by specifying an account environment variable."""
)
@click.option("-C", "--config_saved",
help="""Tell the CLI which account to run the command on by specifying a saved configuration - see
help="""Tell the CLI which account to run the command on by specifying a saved configuration - see
`config` command.""")
@click_log.simple_verbosity_option(logger)
def cli(config, config_saved):
@click.pass_context
def cli(ctx, config, config_saved):
if config:
refresh_cloudinary_config(config)
elif config_saved:
Expand All @@ -40,4 +41,9 @@ def cli(config, config_saved):
if not is_valid_cloudinary_config():
logger.warning("No Cloudinary configuration found.")

# If no subcommand was invoked, show help and exit with code 0
if ctx.invoked_subcommand is None:
click.echo(ctx.get_help())
ctx.exit(0)

return True
2 changes: 1 addition & 1 deletion cloudinary_cli/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from cloudinary_cli.core.utils import url, utils
from cloudinary_cli.core.overrides import resolve_command

setattr(click.MultiCommand, "resolve_command", resolve_command)
setattr(click.Group, "resolve_command", resolve_command)

commands = [
config,
Expand Down
7 changes: 6 additions & 1 deletion cloudinary_cli/core/overrides.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from click.parser import split_opt
from click.utils import make_str
from cloudinary import api, uploader
from cloudinary.uploader import upload as original_upload
from cloudinary.utils import cloudinary_url as original_cloudinary_url
from cloudinary_cli.utils.utils import split_opt


# overrides click.MultiCommand.resolve_command
def resolve_command(self, ctx, args):
# Patch the `resolve_command` function to enable simple commands (eg. cld resource)
# Only core commands from API and modules are registered (eg. cld admin)

# Handle empty args (when CLI is invoked with no arguments)
if not args:
return None, None, []

cmd_name = make_str(args[0])
original_cmd_name = cmd_name

Expand Down
2 changes: 1 addition & 1 deletion cloudinary_cli/modules/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def _normalize_remote_file_names(self, remote_files, local_files):

def _local_candidates(self, candidate_path):
filename, extension = path.splitext(candidate_path)
r = re.compile(f"({candidate_path}|{filename} \(\d+\){extension})")
r = re.compile(f"({candidate_path}|{filename} \\(\\d+\\){extension})")
# sort local files by base name (without ext) for accurate results.
return dict(sorted({f: self.local_files[f]["etag"] for f in filter(r.match, self.local_files.keys())}.items(),
key=lambda f: path.splitext(f[0])[0]))
Expand Down
22 changes: 22 additions & 0 deletions cloudinary_cli/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,25 @@ def duplicate_values(items, value_key, key_of_interest=None):
rev_multidict.setdefault(value[value_key], set()).add(value[key_of_interest] if key_of_interest is not None else key)

return {key: values for key, values in rev_multidict.items() if len(values) > 1}


def split_opt(opt):
"""
Splits an option string into prefix and value parts.

This function replaces the deprecated click.parser.split_opt import.
Returns a tuple of (prefix, value) where prefix is the option prefix
(like '-' or '--') and value is the remaining part, or ('', opt)
if it doesn't look like an option.

:param opt: The option string to parse.
:type opt: str
:return: Tuple of (prefix, value)
:rtype: tuple
"""
first = opt[:1]
if first.isalnum():
return '', opt
if opt[1:2] == first:
return opt[:2], opt[2:]
return first, opt[1:]