Skip to content

Commit 22fc4c4

Browse files
authored
Merge pull request #1102 from cloudbees-oss/LCHIB-641
[LCHIB-641] if '%' is missing on Windows, suggest a likely cause of e…
2 parents 3181a42 + 713247f commit 22fc4c4

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

launchable/utils/click.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,20 @@ class PercentageType(ParamType):
2929

3030
def convert(self, value: str, param: Optional[click.core.Parameter], ctx: Optional[click.core.Context]):
3131
try:
32+
missing_percent = False
3233
if value.endswith('%'):
3334
x = float(value[:-1]) / 100
3435
if 0 <= x <= 100:
3536
return x
37+
else:
38+
missing_percent = True
3639
except ValueError:
3740
pass
3841

39-
self.fail("Expected percentage like 50% but got '{}'".format(value), param, ctx)
42+
msg = "Expected percentage like 50% but got '{}'".format(value)
43+
if missing_percent and sys.platform.startswith("win"):
44+
msg += " ('%' is a special character in batch files, so please write '50%%' to pass in '50%')"
45+
self.fail(msg, param, ctx)
4046

4147

4248
class DurationType(ParamType):

tests/utils/test_click.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import datetime
2+
import sys
23
from datetime import timezone
34
from typing import Sequence, Tuple
45
from unittest import TestCase
@@ -7,7 +8,51 @@
78
from click.testing import CliRunner
89
from dateutil.tz import tzlocal
910

10-
from launchable.utils.click import DATETIME_WITH_TZ, KEY_VALUE, convert_to_seconds
11+
from launchable.utils.click import DATETIME_WITH_TZ, KEY_VALUE, PercentageType, convert_to_seconds
12+
13+
14+
class PercentageTypeTest(TestCase):
15+
ERROR_MSG = "Expected percentage like 50% but got"
16+
WINDOWS_ERROR_MSG = "please write '50%%' to pass in '50%'"
17+
18+
def test_invalid_value_windows(self):
19+
pct = PercentageType()
20+
orig_platform = sys.platform
21+
sys.platform = "win32"
22+
try:
23+
with self.assertRaises(click.BadParameter) as cm:
24+
pct.convert("50", None, None)
25+
msg = str(cm.exception)
26+
self.assertIn(self.ERROR_MSG + " '50'", msg)
27+
self.assertIn(self.WINDOWS_ERROR_MSG, msg)
28+
finally:
29+
sys.platform = orig_platform
30+
31+
def test_invalid_value_non_windows(self):
32+
pct = PercentageType()
33+
orig_platform = sys.platform
34+
sys.platform = "linux"
35+
try:
36+
with self.assertRaises(click.BadParameter) as cm:
37+
pct.convert("50", None, None)
38+
msg = str(cm.exception)
39+
self.assertIn(self.ERROR_MSG + " '50'", msg)
40+
self.assertNotIn(self.WINDOWS_ERROR_MSG, msg)
41+
finally:
42+
sys.platform = orig_platform
43+
44+
def test_invalid_float(self):
45+
pct = PercentageType()
46+
with self.assertRaises(click.BadParameter) as cm:
47+
pct.convert("abc%", None, None)
48+
msg = str(cm.exception)
49+
self.assertIn(self.ERROR_MSG + " 'abc%'", msg)
50+
51+
def test_valid(self):
52+
pct = PercentageType()
53+
self.assertEqual(pct.convert("50%", None, None), 0.5)
54+
self.assertEqual(pct.convert("0%", None, None), 0.0)
55+
self.assertEqual(pct.convert("100%", None, None), 1.0)
1156

1257

1358
class DurationTypeTest(TestCase):

0 commit comments

Comments
 (0)