Skip to content

Commit 71ea421

Browse files
committed
[option] Add validate string to {Numeric, Path}Option
1 parent 1362056 commit 71ea421

4 files changed

Lines changed: 30 additions & 12 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ option = PathOption(name="option-name",
752752
default="path/to/folder/or/file",
753753
empty_ok=False, # is an empty path considered valid?
754754
absolute=False, # is the path relative to the config file?
755+
validate=validate_path,
755756
dependencies=add_option_dependencies)
756757
```
757758

@@ -774,14 +775,15 @@ option = BooleanOption(name="option-name",
774775
This option allows a number from [-Inf, +Inf]. You can limit this to the
775776
range [minimum, maximum]. When using floating point numbers here, please be
776777
aware that not all floating point numbers can be represented as a string
777-
(like "1/3"). The dependency handler is passed a numeric value.
778+
(like "1/3"). The validation and dependency handlers are passed a numeric value.
778779

779780
```python
780781
option = NumericOption(name="option-name",
781782
description="numeric",
782783
minimum=0,
783784
maximum=100,
784785
default=50,
786+
validate=validate_number,
785787
dependencies=add_option_dependencies)
786788
```
787789

lbuild/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from lbuild.api import Builder
2424

25-
__version__ = '1.16.1'
25+
__version__ = '1.17.0'
2626

2727

2828
class InitAction:

lbuild/option.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
class Option(BaseNode):
2727

28-
def __init__(self, name, description, default=None, dependencies=None,
28+
def __init__(self, name, description, default=None, dependencies=None, validate=None,
2929
convert_input=None, convert_output=None):
3030
BaseNode.__init__(self, name, BaseNode.Type.OPTION)
3131
self._dependency_handler = dependencies
@@ -35,6 +35,7 @@ def __init__(self, name, description, default=None, dependencies=None,
3535
self._input = None
3636
self._output = None
3737
self._default = None
38+
self._validate = validate
3839
self._filename = os.path.join(os.getcwd(), "dummy")
3940
self._set_default(default)
4041

@@ -93,24 +94,24 @@ def format_values(self):
9394
class StringOption(Option):
9495

9596
def __init__(self, name, description, default=None, dependencies=None, validate=None):
96-
Option.__init__(self, name, description, None, dependencies,
97+
Option.__init__(self, name, description, None, dependencies, validate,
9798
convert_input=self._validate_string)
98-
self._validate = validate
9999
self._set_default(default)
100100

101101
def _validate_string(self, value):
102102
value = str(value)
103-
if self._validate:
103+
if self._validate is not None:
104104
self._validate(value)
105105
return value
106106

107107

108108
class PathOption(Option):
109109

110-
def __init__(self, name, description, default=None, dependencies=None, empty_ok=False, absolute=False):
110+
def __init__(self, name, description, default=None, empty_ok=False,
111+
absolute=False, dependencies=None, validate=None):
111112
self._empty_ok = empty_ok
112113
self._absolute = absolute
113-
Option.__init__(self, name, description, default, dependencies,
114+
Option.__init__(self, name, description, default, dependencies, validate,
114115
convert_input=self._validate_path,
115116
convert_output=self._relocate_path)
116117

@@ -132,6 +133,8 @@ def _validate_path(self, path):
132133
# Relocate path to be relative to the user's perspective
133134
if self._relocate(path):
134135
path = os.path.relpath(self._relocate_path(path))
136+
if self._validate is not None:
137+
self._validate(path)
135138
return path
136139

137140
@staticmethod
@@ -191,8 +194,8 @@ def as_boolean(value):
191194
class NumericOption(Option):
192195

193196
def __init__(self, name, description, minimum=None, maximum=None,
194-
default=None, dependencies=None):
195-
Option.__init__(self, name, description, default, dependencies,
197+
default=None, dependencies=None, validate=None):
198+
Option.__init__(self, name, description, default, dependencies, validate,
196199
convert_input=str,
197200
convert_output=self.as_numeric_value)
198201
self.minimum_input = str(minimum)
@@ -227,6 +230,8 @@ def value(self, value):
227230
raise ValueError("Input must be greater or equal to '{}'".format(self.minimum))
228231
if self.maximum is not None and numeric_value > self.maximum:
229232
raise ValueError("Input must be smaller or equal to '{}'".format(self.maximum))
233+
if self._validate is not None:
234+
self._validate(numeric_value)
230235
except (TypeError, ValueError) as error:
231236
raise le.LbuildOptionInputException(self, value, error)
232237
self._set_value(value)

test/option_test.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ def validate_string(value):
121121
validate=validate_string)
122122

123123
def test_should_be_constructable_from_path(self):
124-
option = PathOption("test", "description", default="filename.txt")
124+
def validate_path(path):
125+
if path.startswith(".."):
126+
raise ValueError("Path must be local.")
127+
128+
option = PathOption("test", "description", default="filename.txt", validate=validate_path)
125129
self.assertIn("test [PathOption]", option.description)
126130
self.assertEqual("filename.txt", option.value)
127131
option.value = "filename.txt.in"
@@ -147,6 +151,8 @@ def test_should_be_constructable_from_path(self):
147151
option.value = "/folder//"
148152
with self.assertRaises(le.LbuildOptionInputException):
149153
option.value = "//folder/"
154+
with self.assertRaises(le.LbuildOptionInputException):
155+
option.value = "../folder"
150156

151157
option = PathOption("test", "description", default="", empty_ok=True)
152158
self.assertEqual("", option.value)
@@ -190,8 +196,11 @@ def test_should_be_constructable_from_boolean(self):
190196
option.value = "hello"
191197

192198
def test_should_be_constructable_from_number(self):
199+
def validate_number(number):
200+
if number == 69:
201+
raise ValueError("Only clean numbers allowed")
193202
option = NumericOption("test", "description", default=1,
194-
minimum=0, maximum=100)
203+
minimum=0, maximum=100, validate=validate_number)
195204
self.assertIn("test [NumericOption]", option.description)
196205
self.assertEqual(1, option.value)
197206
option.value = 2
@@ -215,6 +224,8 @@ def test_should_be_constructable_from_number(self):
215224
option.value = 1000
216225
with self.assertRaises(le.LbuildOptionInputException):
217226
option.value = "hello"
227+
with self.assertRaises(le.LbuildOptionInputException):
228+
option.value = 69
218229

219230
with self.assertRaises(le.LbuildOptionConstructionException):
220231
NumericOption("test", "description", minimum=0, maximum=0)

0 commit comments

Comments
 (0)