Skip to content

Commit ca3bd63

Browse files
committed
fix(cli): fix parsing CLI objects to classnames
1 parent d9fdf1d commit ca3bd63

File tree

3 files changed

+46
-16
lines changed

3 files changed

+46
-16
lines changed

gitlab/cli.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@
2222
import re
2323
import sys
2424

25+
from requests.structures import CaseInsensitiveDict
26+
2527
import gitlab.config
2628

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

2934
# custom_actions = {
3035
# cls: {
@@ -69,12 +74,20 @@ def die(msg, e=None):
6974
sys.exit(1)
7075

7176

72-
def what_to_cls(what):
73-
return "".join([s.capitalize() for s in what.split("-")])
77+
def what_to_cls(what, namespace):
78+
"""Given a kebab-case string from a CLI argument, return a corresponding
79+
(CamelCase) class in a given namespace with a case-insensitive lookup."""
80+
classes = CaseInsensitiveDict(namespace.__dict__)
81+
lowercase_class = what.replace("-", "")
82+
83+
return classes[lowercase_class]
7484

7585

7686
def cls_to_what(cls):
77-
return camel_re.sub(r"\1-\2", cls.__name__).lower()
87+
"""Convert CamelCase class names to kebab-case in two steps, to ensure names
88+
with whole upper-case words are correctly dash-separated as well."""
89+
dash_upper = camel_upperlower_regex.sub(r"\1-\2", cls.__name__)
90+
return camel_lowerupper_regex.sub(r"\1-\2", dash_upper).lower()
7891

7992

8093
def _get_base_parser(add_help=True):

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)