Skip to content

Commit 32c990f

Browse files
committed
fix(cli): fix parsing CLI objects to classnames
1 parent f909cae commit 32c990f

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

gitlab/cli.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@
2121
import functools
2222
import re
2323
import sys
24-
from typing import Any, Callable, Dict, Tuple
24+
from typing import Any, Callable, Dict, Tuple, Type
25+
26+
from requests.structures import CaseInsensitiveDict
2527

2628
import gitlab.config
2729

28-
camel_re = re.compile("(.)([A-Z])")
30+
# Full credit for this regex goes to:
31+
# https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py
32+
camel_upperlower_regex = re.compile(r"([A-Z]+)([A-Z][a-z])")
33+
camel_lowerupper_regex = re.compile(r"([a-z\d])([A-Z])")
2934

3035
# custom_actions = {
3136
# cls: {
@@ -72,12 +77,20 @@ def die(msg: str, e=None) -> None:
7277
sys.exit(1)
7378

7479

75-
def what_to_cls(what: str) -> str:
76-
return "".join([s.capitalize() for s in what.split("-")])
80+
def what_to_cls(what: str, namespace: Type) -> Type:
81+
"""Given a kebab-case string from a CLI argument, return a corresponding
82+
(CamelCase) class in a given namespace with a case-insensitive lookup."""
83+
classes = CaseInsensitiveDict(namespace.__dict__)
84+
lowercase_class = what.replace("-", "")
85+
86+
return classes[lowercase_class]
7787

7888

7989
def cls_to_what(cls) -> str:
80-
return camel_re.sub(r"\1-\2", cls.__name__).lower()
90+
"""Convert CamelCase class names to kebab-case in two steps, to ensure names
91+
with whole upper-case words are correctly dash-separated as well."""
92+
dash_upper = camel_upperlower_regex.sub(r"\1-\2", cls.__name__)
93+
return camel_lowerupper_regex.sub(r"\1-\2", dash_upper).lower()
8194

8295

8396
def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:

gitlab/tests/test_cli.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,37 @@
2929
import gitlab.v4.cli
3030

3131

32-
def test_what_to_cls():
33-
assert "Foo" == cli.what_to_cls("foo")
34-
assert "FooBar" == cli.what_to_cls("foo-bar")
32+
@pytest.mark.parametrize(
33+
"what,expected_class",
34+
[
35+
("class", "Class"),
36+
("test-class", "TestClass"),
37+
("test-longer-class", "TestLongerClass"),
38+
],
39+
)
40+
def test_what_to_cls(what, expected_class):
41+
def _namespace():
42+
pass
3543

44+
ExpectedClass = type(expected_class, (), {})
45+
_namespace.__dict__[expected_class] = ExpectedClass
3646

37-
def test_cls_to_what():
38-
class Class(object):
39-
pass
47+
assert cli.what_to_cls(what, _namespace) == ExpectedClass
4048

41-
class TestClass(object):
42-
pass
4349

44-
assert "test-class" == cli.cls_to_what(TestClass)
45-
assert "class" == cli.cls_to_what(Class)
50+
@pytest.mark.parametrize(
51+
"class_name,expected_what",
52+
[
53+
("Class", "class"),
54+
("TestClass", "test-class"),
55+
("TestUPPERCASEClass", "test-uppercase-class"),
56+
("UPPERCASETestClass", "uppercase-test-class"),
57+
],
58+
)
59+
def test_cls_to_what(class_name, expected_what):
60+
TestClass = type(class_name, (), {})
61+
62+
assert cli.cls_to_what(TestClass) == expected_what
4663

4764

4865
def test_die():

gitlab/v4/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828

2929
class GitlabCLI(object):
3030
def __init__(self, gl, what, action, args):
31-
self.cls_name = cli.what_to_cls(what)
32-
self.cls = gitlab.v4.objects.__dict__[self.cls_name]
31+
self.cls = cli.what_to_cls(what, namespace=gitlab.v4.objects)
32+
self.cls_name = self.cls.__name__
3333
self.what = what.replace("-", "_")
3434
self.action = action.lower()
3535
self.gl = gl

0 commit comments

Comments
 (0)