Skip to content

Commit da36226

Browse files
authored
Merge pull request #384 from VatsalJagani/float-validator-for-custom-commands
Added Float parameter validator for custom search commands.
2 parents a17e0cd + 3687786 commit da36226

File tree

4 files changed

+122
-3
lines changed

4 files changed

+122
-3
lines changed

docs/searchcommands.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ splunklib.searchcommands
8888
:members:
8989
:inherited-members:
9090

91+
.. autoclass:: Float
92+
:members:
93+
:inherited-members:
94+
9195
.. autoclass:: RegularExpression
9296
:members:
9397
:inherited-members:

splunklib/searchcommands/validators.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,48 @@ def format(self, value):
204204
return None if value is None else six.text_type(int(value))
205205

206206

207+
class Float(Validator):
208+
""" Validates float option values.
209+
210+
"""
211+
def __init__(self, minimum=None, maximum=None):
212+
if minimum is not None and maximum is not None:
213+
def check_range(value):
214+
if not (minimum <= value <= maximum):
215+
raise ValueError('Expected float in the range [{0},{1}], not {2}'.format(minimum, maximum, value))
216+
return
217+
elif minimum is not None:
218+
def check_range(value):
219+
if value < minimum:
220+
raise ValueError('Expected float in the range [{0},+∞], not {1}'.format(minimum, value))
221+
return
222+
elif maximum is not None:
223+
def check_range(value):
224+
if value > maximum:
225+
raise ValueError('Expected float in the range [-∞,{0}], not {1}'.format(maximum, value))
226+
return
227+
else:
228+
def check_range(value):
229+
return
230+
231+
self.check_range = check_range
232+
return
233+
234+
def __call__(self, value):
235+
if value is None:
236+
return None
237+
try:
238+
value = float(value)
239+
except ValueError:
240+
raise ValueError('Expected float value, not {}'.format(json_encode_string(value)))
241+
242+
self.check_range(value)
243+
return value
244+
245+
def format(self, value):
246+
return None if value is None else six.text_type(float(value))
247+
248+
207249
class Duration(Validator):
208250
""" Validates duration option values.
209251
@@ -391,4 +433,4 @@ def format(self, value):
391433
return self.__call__(value)
392434

393435

394-
__all__ = ['Boolean', 'Code', 'Duration', 'File', 'Integer', 'List', 'Map', 'RegularExpression', 'Set']
436+
__all__ = ['Boolean', 'Code', 'Duration', 'File', 'Integer', 'Float', 'List', 'Map', 'RegularExpression', 'Set']

tests/searchcommands/test_decorators.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ class TestSearchCommand(SearchCommand):
121121
**Syntax:** **integer=***<value>*
122122
**Description:** An integer value''',
123123
require=True, validate=validators.Integer())
124+
125+
float = Option(
126+
doc='''
127+
**Syntax:** **float=***<value>*
128+
**Description:** An float value''',
129+
validate=validators.Float())
130+
131+
required_float = Option(
132+
doc='''
133+
**Syntax:** **float=***<value>*
134+
**Description:** An float value''',
135+
require=True, validate=validators.Float())
124136

125137
map = Option(
126138
doc='''
@@ -408,6 +420,7 @@ def test_option(self):
408420
'fieldname': u'some.field_name',
409421
'file': six.text_type(repr(__file__)),
410422
'integer': 100,
423+
'float': 99.9,
411424
'logging_configuration': environment.logging_configuration,
412425
'logging_level': u'WARNING',
413426
'map': 'foo',
@@ -421,6 +434,7 @@ def test_option(self):
421434
'required_fieldname': u'some.field_name',
422435
'required_file': six.text_type(repr(__file__)),
423436
'required_integer': 100,
437+
'required_float': 99.9,
424438
'required_map': 'foo',
425439
'required_match': u'123-45-6789',
426440
'required_optionname': u'some_option_name',
@@ -452,10 +466,10 @@ def test_option(self):
452466

453467
expected = (
454468
'foo="f" boolean="f" code="foo == \\"bar\\"" duration="24:59:59" fieldname="some.field_name" '
455-
'file=' + json_encode_string(__file__) + ' integer="100" map="foo" match="123-45-6789" '
469+
'file=' + json_encode_string(__file__) + ' integer="100" float="99.9" map="foo" match="123-45-6789" '
456470
'optionname="some_option_name" record="f" regularexpression="\\\\s+" required_boolean="f" '
457471
'required_code="foo == \\"bar\\"" required_duration="24:59:59" required_fieldname="some.field_name" '
458-
'required_file=' + json_encode_string(__file__) + ' required_integer="100" required_map="foo" '
472+
'required_file=' + json_encode_string(__file__) + ' required_integer="100" required_float="99.9" required_map="foo" '
459473
'required_match="123-45-6789" required_optionname="some_option_name" required_regularexpression="\\\\s+" '
460474
'required_set="bar" set="bar" show_configuration="f"')
461475

tests/searchcommands/test_validators.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,65 @@ def test(integer):
205205
self.assertRaises(ValueError, validator.__call__, maxsize + 1)
206206

207207
return
208+
209+
def test_float(self):
210+
# Float validator test
211+
212+
maxsize = sys.maxsize
213+
minsize = -(sys.maxsize - 1)
214+
215+
validator = validators.Float()
216+
217+
def test(float_val):
218+
try:
219+
float_val = float(float_val)
220+
except ValueError:
221+
assert False
222+
for s in str(float_val), six.text_type(float_val):
223+
value = validator.__call__(s)
224+
self.assertEqual(value, float_val)
225+
self.assertIsInstance(value, float)
226+
self.assertEqual(validator.format(float_val), six.text_type(float_val))
227+
228+
test(2 * minsize)
229+
test(minsize)
230+
test(-1)
231+
test(0)
232+
test(-1.12345)
233+
test(0.0001)
234+
test(100101.011)
235+
test(2 * maxsize)
236+
test('18.32123')
237+
self.assertRaises(ValueError, validator.__call__, 'Splunk!')
238+
239+
validator = validators.Float(minimum=0)
240+
self.assertEqual(validator.__call__(0), 0)
241+
self.assertEqual(validator.__call__(1.154), 1.154)
242+
self.assertEqual(validator.__call__(888.51), 888.51)
243+
self.assertEqual(validator.__call__(2 * maxsize), float(2 * maxsize))
244+
self.assertRaises(ValueError, validator.__call__, -1)
245+
self.assertRaises(ValueError, validator.__call__, -1111.00578)
246+
self.assertRaises(ValueError, validator.__call__, -0.005)
247+
248+
validator = validators.Float(minimum=1, maximum=maxsize)
249+
self.assertEqual(validator.__call__(1), float(1))
250+
self.assertEqual(validator.__call__(100.111), 100.111)
251+
self.assertEqual(validator.__call__(9999.0), 9999.0)
252+
self.assertEqual(validator.__call__(maxsize), float(maxsize))
253+
self.assertRaises(ValueError, validator.__call__, 0)
254+
self.assertRaises(ValueError, validator.__call__, 0.9999)
255+
self.assertRaises(ValueError, validator.__call__, -199)
256+
self.assertRaises(ValueError, validator.__call__, maxsize + 1)
257+
258+
validator = validators.Float(minimum=-1, maximum=1)
259+
self.assertEqual(validator.__call__(0), float(0))
260+
self.assertEqual(validator.__call__(0.123456), 0.123456)
261+
self.assertEqual(validator.__call__(-0.012), -0.012)
262+
self.assertRaises(ValueError, validator.__call__, -1.1)
263+
self.assertRaises(ValueError, validator.__call__, 100.123456)
264+
self.assertRaises(ValueError, validator.__call__, maxsize + 1)
265+
266+
return
208267

209268
def test_list(self):
210269

0 commit comments

Comments
 (0)