diff --git a/docs/searchcommands.rst b/docs/searchcommands.rst index 281f755f..e70abf49 100644 --- a/docs/searchcommands.rst +++ b/docs/searchcommands.rst @@ -88,6 +88,10 @@ splunklib.searchcommands :members: :inherited-members: +.. autoclass:: Float + :members: + :inherited-members: + .. autoclass:: RegularExpression :members: :inherited-members: diff --git a/splunklib/searchcommands/validators.py b/splunklib/searchcommands/validators.py index 0278fbd5..df1872db 100644 --- a/splunklib/searchcommands/validators.py +++ b/splunklib/searchcommands/validators.py @@ -204,6 +204,48 @@ def format(self, value): return None if value is None else six.text_type(int(value)) +class Float(Validator): + """ Validates float option values. + + """ + def __init__(self, minimum=None, maximum=None): + if minimum is not None and maximum is not None: + def check_range(value): + if not (minimum <= value <= maximum): + raise ValueError('Expected float in the range [{0},{1}], not {2}'.format(minimum, maximum, value)) + return + elif minimum is not None: + def check_range(value): + if value < minimum: + raise ValueError('Expected float in the range [{0},+∞], not {1}'.format(minimum, value)) + return + elif maximum is not None: + def check_range(value): + if value > maximum: + raise ValueError('Expected float in the range [-∞,{0}], not {1}'.format(maximum, value)) + return + else: + def check_range(value): + return + + self.check_range = check_range + return + + def __call__(self, value): + if value is None: + return None + try: + value = float(value) + except ValueError: + raise ValueError('Expected float value, not {}'.format(json_encode_string(value))) + + self.check_range(value) + return value + + def format(self, value): + return None if value is None else six.text_type(float(value)) + + class Duration(Validator): """ Validates duration option values. @@ -391,4 +433,4 @@ def format(self, value): return self.__call__(value) -__all__ = ['Boolean', 'Code', 'Duration', 'File', 'Integer', 'List', 'Map', 'RegularExpression', 'Set'] +__all__ = ['Boolean', 'Code', 'Duration', 'File', 'Integer', 'Float', 'List', 'Map', 'RegularExpression', 'Set'] diff --git a/tests/searchcommands/test_decorators.py b/tests/searchcommands/test_decorators.py index 84900d41..082ab184 100755 --- a/tests/searchcommands/test_decorators.py +++ b/tests/searchcommands/test_decorators.py @@ -121,6 +121,18 @@ class TestSearchCommand(SearchCommand): **Syntax:** **integer=**** **Description:** An integer value''', require=True, validate=validators.Integer()) + + float = Option( + doc=''' + **Syntax:** **float=**** + **Description:** An float value''', + validate=validators.Float()) + + required_float = Option( + doc=''' + **Syntax:** **float=**** + **Description:** An float value''', + require=True, validate=validators.Float()) map = Option( doc=''' @@ -408,6 +420,7 @@ def test_option(self): 'fieldname': u'some.field_name', 'file': six.text_type(repr(__file__)), 'integer': 100, + 'float': 99.9, 'logging_configuration': environment.logging_configuration, 'logging_level': u'WARNING', 'map': 'foo', @@ -421,6 +434,7 @@ def test_option(self): 'required_fieldname': u'some.field_name', 'required_file': six.text_type(repr(__file__)), 'required_integer': 100, + 'required_float': 99.9, 'required_map': 'foo', 'required_match': u'123-45-6789', 'required_optionname': u'some_option_name', @@ -452,10 +466,10 @@ def test_option(self): expected = ( 'foo="f" boolean="f" code="foo == \\"bar\\"" duration="24:59:59" fieldname="some.field_name" ' - 'file=' + json_encode_string(__file__) + ' integer="100" map="foo" match="123-45-6789" ' + 'file=' + json_encode_string(__file__) + ' integer="100" float="99.9" map="foo" match="123-45-6789" ' 'optionname="some_option_name" record="f" regularexpression="\\\\s+" required_boolean="f" ' 'required_code="foo == \\"bar\\"" required_duration="24:59:59" required_fieldname="some.field_name" ' - 'required_file=' + json_encode_string(__file__) + ' required_integer="100" required_map="foo" ' + 'required_file=' + json_encode_string(__file__) + ' required_integer="100" required_float="99.9" required_map="foo" ' 'required_match="123-45-6789" required_optionname="some_option_name" required_regularexpression="\\\\s+" ' 'required_set="bar" set="bar" show_configuration="f"') diff --git a/tests/searchcommands/test_validators.py b/tests/searchcommands/test_validators.py index f174ad21..8532fa42 100755 --- a/tests/searchcommands/test_validators.py +++ b/tests/searchcommands/test_validators.py @@ -205,6 +205,65 @@ def test(integer): self.assertRaises(ValueError, validator.__call__, maxsize + 1) return + + def test_float(self): + # Float validator test + + maxsize = sys.maxsize + minsize = -(sys.maxsize - 1) + + validator = validators.Float() + + def test(float_val): + try: + float_val = float(float_val) + except ValueError: + assert False + for s in str(float_val), six.text_type(float_val): + value = validator.__call__(s) + self.assertEqual(value, float_val) + self.assertIsInstance(value, float) + self.assertEqual(validator.format(float_val), six.text_type(float_val)) + + test(2 * minsize) + test(minsize) + test(-1) + test(0) + test(-1.12345) + test(0.0001) + test(100101.011) + test(2 * maxsize) + test('18.32123') + self.assertRaises(ValueError, validator.__call__, 'Splunk!') + + validator = validators.Float(minimum=0) + self.assertEqual(validator.__call__(0), 0) + self.assertEqual(validator.__call__(1.154), 1.154) + self.assertEqual(validator.__call__(888.51), 888.51) + self.assertEqual(validator.__call__(2 * maxsize), float(2 * maxsize)) + self.assertRaises(ValueError, validator.__call__, -1) + self.assertRaises(ValueError, validator.__call__, -1111.00578) + self.assertRaises(ValueError, validator.__call__, -0.005) + + validator = validators.Float(minimum=1, maximum=maxsize) + self.assertEqual(validator.__call__(1), float(1)) + self.assertEqual(validator.__call__(100.111), 100.111) + self.assertEqual(validator.__call__(9999.0), 9999.0) + self.assertEqual(validator.__call__(maxsize), float(maxsize)) + self.assertRaises(ValueError, validator.__call__, 0) + self.assertRaises(ValueError, validator.__call__, 0.9999) + self.assertRaises(ValueError, validator.__call__, -199) + self.assertRaises(ValueError, validator.__call__, maxsize + 1) + + validator = validators.Float(minimum=-1, maximum=1) + self.assertEqual(validator.__call__(0), float(0)) + self.assertEqual(validator.__call__(0.123456), 0.123456) + self.assertEqual(validator.__call__(-0.012), -0.012) + self.assertRaises(ValueError, validator.__call__, -1.1) + self.assertRaises(ValueError, validator.__call__, 100.123456) + self.assertRaises(ValueError, validator.__call__, maxsize + 1) + + return def test_list(self):