summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNejc Habjan <hab.nejc@gmail.com>2021-02-10 00:32:48 +0100
committerNejc Habjan <hab.nejc@gmail.com>2021-03-07 11:58:53 +0100
commitf335435f8e2c4a6136388cfdcbc21795071b5810 (patch)
tree0fd38d2f67a72847f327f063311397fe6f7c00a7
parent48fc907403b630f069dfd63fada73f96a8c6e983 (diff)
downloadgitlab-f335435f8e2c4a6136388cfdcbc21795071b5810.tar.gz
fix(cli): fix parsing CLI objects to classnames
-rw-r--r--gitlab/cli.py26
-rw-r--r--gitlab/tests/test_cli.py37
-rw-r--r--gitlab/v4/cli.py4
3 files changed, 49 insertions, 18 deletions
diff --git a/gitlab/cli.py b/gitlab/cli.py
index bd2c13d..6db8d61 100644
--- a/gitlab/cli.py
+++ b/gitlab/cli.py
@@ -21,11 +21,17 @@ import argparse
import functools
import re
import sys
-from typing import Any, Callable, Dict, Optional, Tuple, Union
+from typing import Callable, Dict, Optional, Tuple, Type, Union
+from requests.structures import CaseInsensitiveDict
+
+from gitlab.base import RESTObject
import gitlab.config
-camel_re = re.compile("(.)([A-Z])")
+# Full credit for this regex goes to:
+# https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py
+camel_upperlower_regex = re.compile(r"([A-Z]+)([A-Z][a-z])")
+camel_lowerupper_regex = re.compile(r"([a-z\d])([A-Z])")
# custom_actions = {
# cls: {
@@ -75,12 +81,20 @@ def die(msg: str, e: Optional[Exception] = None) -> None:
sys.exit(1)
-def what_to_cls(what: str) -> str:
- return "".join([s.capitalize() for s in what.split("-")])
+def what_to_cls(what: str, namespace: Type) -> RESTObject:
+ """Given a kebab-case string from a CLI argument, return a corresponding
+ (CamelCase) class in a given namespace with a case-insensitive lookup."""
+ classes = CaseInsensitiveDict(namespace.__dict__)
+ lowercase_class = what.replace("-", "")
+
+ return classes[lowercase_class]
-def cls_to_what(cls: Any) -> str:
- return camel_re.sub(r"\1-\2", cls.__name__).lower()
+def cls_to_what(cls: RESTObject) -> str:
+ """Convert CamelCase class names to kebab-case in two steps, to ensure names
+ with whole upper-case words are correctly dash-separated as well."""
+ dash_upper = camel_upperlower_regex.sub(r"\1-\2", cls.__name__)
+ return camel_lowerupper_regex.sub(r"\1-\2", dash_upper).lower()
def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
diff --git a/gitlab/tests/test_cli.py b/gitlab/tests/test_cli.py
index 2246369..12e3e42 100644
--- a/gitlab/tests/test_cli.py
+++ b/gitlab/tests/test_cli.py
@@ -29,20 +29,37 @@ from gitlab import cli
import gitlab.v4.cli
-def test_what_to_cls():
- assert "Foo" == cli.what_to_cls("foo")
- assert "FooBar" == cli.what_to_cls("foo-bar")
+@pytest.mark.parametrize(
+ "what,expected_class",
+ [
+ ("class", "Class"),
+ ("test-class", "TestClass"),
+ ("test-longer-class", "TestLongerClass"),
+ ],
+)
+def test_what_to_cls(what, expected_class):
+ def _namespace():
+ pass
+ ExpectedClass = type(expected_class, (), {})
+ _namespace.__dict__[expected_class] = ExpectedClass
-def test_cls_to_what():
- class Class(object):
- pass
+ assert cli.what_to_cls(what, _namespace) == ExpectedClass
- class TestClass(object):
- pass
- assert "test-class" == cli.cls_to_what(TestClass)
- assert "class" == cli.cls_to_what(Class)
+@pytest.mark.parametrize(
+ "class_name,expected_what",
+ [
+ ("Class", "class"),
+ ("TestClass", "test-class"),
+ ("TestUPPERCASEClass", "test-uppercase-class"),
+ ("UPPERCASETestClass", "uppercase-test-class"),
+ ],
+)
+def test_cls_to_what(class_name, expected_what):
+ TestClass = type(class_name, (), {})
+
+ assert cli.cls_to_what(TestClass) == expected_what
def test_die():
diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py
index 6172f93..20b3907 100644
--- a/gitlab/v4/cli.py
+++ b/gitlab/v4/cli.py
@@ -28,8 +28,8 @@ import gitlab.v4.objects
class GitlabCLI(object):
def __init__(self, gl, what, action, args):
- self.cls_name = cli.what_to_cls(what)
- self.cls = gitlab.v4.objects.__dict__[self.cls_name]
+ self.cls = cli.what_to_cls(what, namespace=gitlab.v4.objects)
+ self.cls_name = self.cls.__name__
self.what = what.replace("-", "_")
self.action = action.lower()
self.gl = gl