diff --git a/.vscode/settings.json b/.vscode/settings.json index 100eba6..e548495 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,9 @@ "editor.renderWhitespace": "all", "editor.rulers": [ 76 ], "editor.tabSize": 2, + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "git.mergeEditor": false, "files.exclude": { "**/__pycache__": true, "**/*.egg-info": true, diff --git a/CHANGES.md b/CHANGES.md index 664b2f5..19ff324 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ # **CLASP.Python** Changes +## 0.8.12 - 28th July 2026 + +* fixed falsy default values causing `MissingValueException` for option arguments; +* fixed failure message to be passed to `MissingValueException`; + + ## 0.8.11 - 27th July 2026 * ~ packaging & boilerplate improvements: diff --git a/pyclasp/__init__.py b/pyclasp/__init__.py index 7d1f47d..30977d0 100644 --- a/pyclasp/__init__.py +++ b/pyclasp/__init__.py @@ -10,7 +10,7 @@ __license__ = "BSD-3-Clause" __maintainer__ = "Matt Wilson" __status__ = "Beta" -__version__ = "0.8.11" +__version__ = "0.8.12" from .exceptions import * from .flag_specification import FlagSpecification, flag diff --git a/pyclasp/option_argument.py b/pyclasp/option_argument.py index 7b5531e..67ab05f 100644 --- a/pyclasp/option_argument.py +++ b/pyclasp/option_argument.py @@ -85,7 +85,7 @@ def _set_value(self, value, from_ctor=False): given_value = arg_spec.default_value - if given_value: + if given_value is not None: if arg_spec.value_type: @@ -148,7 +148,23 @@ def _set_value(self, value, from_ctor=False): pass else: - raise MissingValueException("the '%s' option does not have a value to be interpreted as an integer" % (self.name)) + if arg_spec.value_type == bool: + + type_name = "boolean" + elif arg_spec.value_type == float: + + type_name = "a number" + elif arg_spec.value_type == int: + + type_name = "an integer" + elif _SUPPORT_long and arg_spec.value_type == long: + + type_name = "a long integer" + else: + + type_name = "a value" + + raise MissingValueException("the '%s' option does not have a value to be interpreted as %s" % (self.name, type_name)) self.given_value = given_value diff --git a/setup.py b/setup.py index 1901d62..d394e62 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name="pyclasp", python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*', - version='0.8.11', + version='0.8.12', author="Matt Wilson", author_email="matthew@synesis.com.au", diff --git a/tests/test_typed_values.py b/tests/test_typed_values.py index 308bbe4..3ca005b 100755 --- a/tests/test_typed_values.py +++ b/tests/test_typed_values.py @@ -303,10 +303,44 @@ def test_empty_option_value_of_type_int(self): argv = ( "myprog", "--length=" ) - with self.assertRaises(clasp.MissingValueException): + with self.assertRaises(clasp.MissingValueException) as ctx: clasp.parse(argv, specifications) + self.assertEqual("the '--length' option does not have a value to be interpreted as an integer", str(ctx.exception)) + + + def test_empty_option_value_of_type_float(self): + + specifications = ( + + clasp.option('--length', alias='-l', value_type=float), + ) + + argv = ( 'myprog', '--length=' ) + + with self.assertRaises(clasp.MissingValueException) as ctx: + + clasp.parse(argv, specifications) + + self.assertEqual("the '--length' option does not have a value to be interpreted as a number", str(ctx.exception)) + + + def test_empty_option_value_of_type_bool(self): + + specifications = ( + + clasp.option('--length', alias='-l', value_type=bool), + ) + + argv = ( 'myprog', '--length=' ) + + with self.assertRaises(clasp.MissingValueException) as ctx: + + clasp.parse(argv, specifications) + + self.assertEqual("the '--length' option does not have a value to be interpreted as boolean", str(ctx.exception)) + def test_empty_option_value_of_type_int_2(self): @@ -442,6 +476,61 @@ def test_flags_of_flags_and_options_combined(self): self.assertEqual(0, len(args.values)) + def test_reproduce_falsy_default_issue(self): + + specifications = ( + clasp.option( + "--depth", + alias="-d", + default=0, + value_type=int, + ), + clasp.option( + "--exchange", + alias="-e", + required=True, + value_type=str, + ), + ) + + argv = ('liquidity-assessor', '-e', 'bybit', '-d', '10') + args = clasp.parse(argv, specifications) + + self.assertEqual(2, len(args.options)) + + depth_option = next(opt for opt in args.options if opt.name == '--depth') + self.assertEqual(10, depth_option.value) + + exchange_option = next(opt for opt in args.options if opt.name == '--exchange') + self.assertEqual('bybit', exchange_option.value) + + + def test_reproduce_falsy_default_issue_with_default_used(self): + + specifications = ( + clasp.option( + "--depth", + alias="-d", + default=0, + value_type=int, + ), + clasp.option( + "--exchange", + alias="-e", + required=True, + value_type=str, + ), + ) + + argv = ('liquidity-assessor', '-e', 'bybit') + args = clasp.parse(argv, specifications) + + # In CLASP, options with defaults are not automatically added to args.options unless specified on the CLI or if we lookup/access them? + # Wait, let's verify if they are added or not. + self.assertEqual(1, len(args.options)) + exchange_option = args.options[0] + self.assertEqual('bybit', exchange_option.value) + if '__main__' == __name__: