squirrels 0.1.1.post1__py3-none-any.whl → 0.2.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of squirrels might be problematic. Click here for more details.

Files changed (74) hide show
  1. squirrels/__init__.py +10 -16
  2. squirrels/_api_server.py +234 -80
  3. squirrels/_authenticator.py +84 -0
  4. squirrels/_command_line.py +60 -72
  5. squirrels/_connection_set.py +96 -0
  6. squirrels/_constants.py +114 -33
  7. squirrels/_environcfg.py +77 -0
  8. squirrels/_initializer.py +126 -67
  9. squirrels/_manifest.py +195 -168
  10. squirrels/_models.py +495 -0
  11. squirrels/_package_loader.py +26 -0
  12. squirrels/_parameter_configs.py +401 -0
  13. squirrels/_parameter_sets.py +188 -0
  14. squirrels/_py_module.py +60 -0
  15. squirrels/_timer.py +36 -0
  16. squirrels/_utils.py +81 -49
  17. squirrels/_version.py +2 -2
  18. squirrels/arguments/init_time_args.py +32 -0
  19. squirrels/arguments/run_time_args.py +82 -0
  20. squirrels/data_sources.py +380 -155
  21. squirrels/dateutils.py +86 -57
  22. squirrels/package_data/base_project/Dockerfile +15 -0
  23. squirrels/package_data/base_project/connections.yml +7 -0
  24. squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
  25. squirrels/package_data/base_project/environcfg.yml +29 -0
  26. squirrels/package_data/base_project/ignores/.dockerignore +8 -0
  27. squirrels/package_data/base_project/ignores/.gitignore +7 -0
  28. squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
  29. squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
  30. squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
  31. squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
  32. squirrels/package_data/base_project/parameters.yml +109 -0
  33. squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
  34. squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
  35. squirrels/package_data/base_project/pyconfigs/context.py +45 -0
  36. squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
  37. squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
  38. squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
  39. squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
  40. squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
  41. squirrels/package_data/base_project/tmp/.gitignore +2 -0
  42. squirrels/package_data/static/script.js +159 -63
  43. squirrels/package_data/static/style.css +79 -15
  44. squirrels/package_data/static/widgets.js +133 -0
  45. squirrels/package_data/templates/index.html +65 -23
  46. squirrels/package_data/templates/index2.html +22 -0
  47. squirrels/parameter_options.py +216 -119
  48. squirrels/parameters.py +407 -478
  49. squirrels/user_base.py +58 -0
  50. squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
  51. squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
  52. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
  53. squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
  54. squirrels/_credentials_manager.py +0 -87
  55. squirrels/_module_loader.py +0 -37
  56. squirrels/_parameter_set.py +0 -151
  57. squirrels/_renderer.py +0 -286
  58. squirrels/_timed_imports.py +0 -37
  59. squirrels/connection_set.py +0 -126
  60. squirrels/package_data/base_project/.gitignore +0 -4
  61. squirrels/package_data/base_project/connections.py +0 -20
  62. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
  63. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
  64. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
  65. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
  66. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
  67. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
  68. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
  69. squirrels/package_data/base_project/squirrels.yaml +0 -22
  70. squirrels-0.1.1.post1.dist-info/METADATA +0 -67
  71. squirrels-0.1.1.post1.dist-info/RECORD +0 -40
  72. squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
  73. squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
  74. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/LICENSE +0 -0
@@ -1,42 +1,58 @@
1
- from typing import Iterable, Optional, Union, Dict, Any
1
+ from typing import Set, Iterable, Optional, Union, Any
2
2
  from dataclasses import dataclass, field
3
3
  from decimal import Decimal, InvalidOperation as InvalidDecimalConversion
4
- from datetime import datetime
4
+ from datetime import datetime, date
5
+ from abc import ABCMeta, abstractmethod
5
6
 
6
- from squirrels._utils import ConfigurationError
7
+ from ._utils import ConfigurationError
7
8
 
8
9
  Number = Union[Decimal, int, str]
9
10
 
10
11
 
11
12
  @dataclass
12
- class ParameterOption:
13
+ class ParameterOption(metaclass=ABCMeta):
13
14
  """
14
- Abstract class (or type) for parameter options
15
+ Abstract class for parameter options
15
16
  """
16
- def __post_init__(self) -> None:
17
- if not hasattr(self, "parent_option_ids"):
18
- self.parent_option_ids = frozenset()
19
-
20
- self.parent_option_ids = frozenset(
21
- {self.parent_option_id} \
22
- if hasattr(self, "parent_option_id") and self.parent_option_id is not None \
23
- else self.parent_option_ids
24
- )
17
+ _user_groups: Set[str] # = field(default_factory=frozenset, kw_only=True)
18
+ _parent_option_ids: Set[str] # = field(default_factory=frozenset, kw_only=True)
19
+
20
+ @abstractmethod
21
+ def __init__(
22
+ self, *, user_groups: Union[Iterable[str], str] = frozenset(), parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
23
+ ) -> None:
24
+ self._user_groups = frozenset({user_groups} if isinstance(user_groups, str) else user_groups)
25
+ self._parent_option_ids = frozenset({parent_option_ids} if isinstance(parent_option_ids, str) else parent_option_ids)
25
26
 
26
- def is_valid(self, selected_parent_option_ids: Optional[Iterable[str]] = None):
27
+ def _validate_lower_upper_values(self, lower_label: str, lower_value: Union[Decimal, date],
28
+ upper_label: str, upper_value: Union[Decimal, date]):
29
+ if lower_value > upper_value:
30
+ raise ConfigurationError(f'The {lower_label} "{lower_value}" must be less than or equal to the {upper_label} "{upper_value}"')
31
+
32
+ def _is_valid(self, user_group: Optional[str], selected_parent_option_ids: Optional[Iterable[str]]) -> bool:
27
33
  """
28
- Checks if this option is valid given the selected parent options.
34
+ Checks if this option is valid given the selected parent options and user group of user if applicable.
29
35
 
30
36
  Parameters:
31
- selected_parent_option_ids: List of selected option ids from the parent parameter
37
+ user_group: The value of the user's "user group attribute". Only None when "user_group_attr" is not specified
38
+ for the Parameter object. Note that when user is None but "user_group_attr" is specified, an error is thrown
39
+ selected_parent_option_ids: List of selected option ids from the parent parameter. Only None when the Parameter
40
+ object has no parent parameter.
32
41
 
33
42
  Returns:
34
43
  True if valid, False otherwise
35
44
  """
36
- if selected_parent_option_ids is not None:
37
- return not self.parent_option_ids.isdisjoint(selected_parent_option_ids)
38
- else:
39
- return True
45
+ if user_group is not None and user_group not in self._user_groups:
46
+ return False
47
+
48
+ if selected_parent_option_ids is not None and self._parent_option_ids.isdisjoint(selected_parent_option_ids):
49
+ return False
50
+
51
+ return True
52
+
53
+ @abstractmethod
54
+ def _to_json_dict(self):
55
+ return {}
40
56
 
41
57
 
42
58
  @dataclass
@@ -45,92 +61,148 @@ class SelectParameterOption(ParameterOption):
45
61
  Parameter option for a select parameter
46
62
 
47
63
  Attributes:
64
+ identifier: Unique identifier for this option that never changes over time
65
+ label: Human readable label that gets shown as a dropdown option
66
+ is_default: True if this is a default option, False otherwise
67
+ user_groups: The user groups this parameter option would show for if "user_group_attr" is specified in the Parameter object
68
+ parent_option_ids: Set of parent option ids this parameter option would show for if "parent" is specified in the Parameter object
69
+ custom_fields: Dictionary to associate custom attributes to the parameter option
48
70
  """
49
- identifier: str
50
- label: str
51
- is_default: bool = False
52
- parent_option_id: Optional[str] = field(default=None, repr=False)
53
- parent_option_ids: Iterable[str] = frozenset()
71
+ _identifier: str
72
+ _label: str
73
+ _is_default: bool # = field(default=False, kw_only=True)
74
+ custom_fields: dict[str, Any] # = field(default_factory=False, kw_only=True)
54
75
 
55
- def __init__(self, identifier: str, label: str, *, is_default: bool = False,
56
- parent_option_id: Optional[str] = None, parent_option_ids: Iterable[str] = frozenset(),
57
- custom_fields: Dict[str, Any] = {}, **kwargs):
76
+ def __init__(
77
+ self, id: str, label: str, *, is_default: bool = False, user_groups: Union[Iterable[str], str] = frozenset(),
78
+ parent_option_ids: Union[Iterable[str], str] = frozenset(), custom_fields: dict[str, Any] = {}, **kwargs
79
+ ) -> None:
58
80
  """
59
81
  Constructor for SelectParameterOption
60
82
 
61
83
  Parameters:
62
- identifier: Unique identifier for this option that never changes over time
63
- label: Human readable label that gets shown as a dropdown option
64
- is_default: True if this is a default option, False otherwise
65
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
66
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None)
67
- custom_fields: Dictionary to associate custom attributes to the parameter option
84
+ ...see Attributes of SelectParameterOption
68
85
  **kwargs: Any additional keyword arguments specified (except the ones above) gets included into custom_fields as well
69
86
  """
70
- self.identifier = identifier
71
- self.label = label
72
- self.is_default = is_default
73
- self.parent_option_id = parent_option_id
74
- self.parent_option_ids = parent_option_ids
87
+ super().__init__(user_groups=user_groups, parent_option_ids=parent_option_ids)
88
+ self._identifier = id
89
+ self._label = label
90
+ self._is_default = is_default
75
91
  self.custom_fields = {
76
- **kwargs, **custom_fields, "id": identifier, "label": label
92
+ **kwargs, **custom_fields, **self._to_json_dict()
77
93
  }
78
- super().__post_init__()
79
94
 
80
- def get_custom_field(self, field: str, default_field: Optional[str] = None, default: Any = None) -> Any:
95
+ def get_custom_field(self, field: str, *, default_field: Optional[str] = None, default: Any = None, **kwargs) -> Any:
81
96
  """
82
97
  Get field value from the custom_fields attribute
83
98
 
84
99
  Parameters:
85
100
  field: The key to use to fetch the custom field from "custom_fields"
86
- default_field: If field does not exist in "custom_fields", then this is used instead as the field (if not None)
87
- default: If field does not exist and default_field is None, then this value is used as default
101
+ default_field: If value at "field" key does not exist in "custom_fields", then this is used instead as the field (if not None)
102
+ default: If value at "field" or "default_field" (if not None) key does not exist in "custom_fields", then this value
103
+ is used as default, or throws an error if None
88
104
 
89
105
  Returns:
90
106
  The type of the custom field
91
107
  """
92
108
  if default_field is not None:
93
- default = self.get_custom_field(default_field)
94
- try:
95
- if default is not None:
96
- selected_field = self.custom_fields.get(field, default)
97
- else:
109
+ default = self.get_custom_field(default_field, default=default)
110
+
111
+ if default is not None:
112
+ selected_field = self.custom_fields.get(field, default)
113
+ else:
114
+ try:
98
115
  selected_field = self.custom_fields[field]
99
- except KeyError as e:
100
- raise ConfigurationError(f"Field '{field}' must exist for parameter option '{self.to_dict()}'") from e
116
+ except KeyError as e:
117
+ raise ConfigurationError(f"Field '{field}' must exist for parameter option {self._to_json_dict()}") from e
101
118
 
102
119
  return selected_field
103
120
 
104
- def to_dict(self):
105
- return {'id': self.identifier, 'label': self.label}
121
+ def _to_json_dict(self):
122
+ return {'id': self._identifier, 'label': self._label}
106
123
 
107
124
 
108
125
  @dataclass
109
- class DateParameterOption(ParameterOption):
126
+ class _DateTypeParameterOption(ParameterOption):
110
127
  """
111
- Parameter option for default dates if it varies based on selection of another parameter
128
+ Abstract class (or type) for date type parameter options
129
+ """
130
+ _date_format: str # = field(default="%Y-%m-%d", kw_only=True)
112
131
 
113
- Attributes:
114
- default_date: Default date for this option
115
- date_format: Format of the default date, default is '%Y-%m-%d'
116
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
117
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None)
118
- """
119
- default_date: Union[str, datetime]
120
- date_format: str = '%Y-%m-%d'
121
- parent_option_id: Optional[str] = field(default=None, repr=False)
122
- parent_option_ids: Iterable[str] = frozenset()
123
-
124
- def __post_init__(self) -> None:
125
- super().__post_init__()
126
- self.default_date = self._validate_date(self.default_date) \
127
- if isinstance(self.default_date, str) else self.default_date
132
+ @abstractmethod
133
+ def __init__(
134
+ self, *, date_format: str = '%Y-%m-%d', user_groups: Union[Iterable[str], str] = frozenset(),
135
+ parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
136
+ ) -> None:
137
+ super().__init__(user_groups=user_groups, parent_option_ids=parent_option_ids)
138
+ self._date_format = date_format
128
139
 
129
- def _validate_date(self, date_str: str) -> datetime:
140
+ def _validate_date(self, date_str: Union[str, date]) -> date:
130
141
  try:
131
- return datetime.strptime(date_str, self.date_format)
142
+ return datetime.strptime(date_str, self._date_format).date() if isinstance(date_str, str) else date_str
132
143
  except ValueError as e:
133
144
  raise ConfigurationError(f'Invalid format for date "{date_str}".') from e
145
+
146
+ def _to_json_dict(self):
147
+ return {}
148
+
149
+
150
+ @dataclass
151
+ class DateParameterOption(_DateTypeParameterOption):
152
+ """
153
+ Parameter option for default dates if it varies based on selection of another parameter
154
+
155
+ Attributes:
156
+ default_date: Default date for this option
157
+ date_format: Format of the default date, default is '%Y-%m-%d'
158
+ user_groups: The user groups this parameter option would show for if "user_group_attr" is specified in the Parameter object
159
+ parent_option_ids: Set of parent option ids this parameter option would show for if "parent" is specified in the Parameter object
160
+ """
161
+ _default_date: date
162
+
163
+ def __init__(
164
+ self, default_date: Union[str, date], *, date_format: str = '%Y-%m-%d', user_groups: Union[Iterable[str], str] = frozenset(),
165
+ parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
166
+ ) -> None:
167
+ """
168
+ Constructor for DateParameterOption
169
+
170
+ Parameters:
171
+ ...see Attributes of DateParameterOption
172
+ """
173
+ super().__init__(date_format=date_format, user_groups=user_groups, parent_option_ids=parent_option_ids)
174
+ self._default_date = self._validate_date(default_date)
175
+
176
+
177
+ @dataclass
178
+ class DateRangeParameterOption(_DateTypeParameterOption):
179
+ """
180
+ Parameter option for default dates if it varies based on selection of another parameter
181
+
182
+ Attributes:
183
+ default_start_date: Default start date for this option
184
+ default_end_date: Default end date for this option
185
+ date_format: Format of the default date, default is '%Y-%m-%d'
186
+ user_groups: The user groups this parameter option would show for if "user_group_attr" is specified in the Parameter object
187
+ parent_option_ids: Set of parent option ids this parameter option would show for if "parent" is specified in the Parameter object
188
+ """
189
+ _default_start_date: date
190
+ _default_end_date: date
191
+
192
+ def __init__(
193
+ self, default_start_date: Union[str, date], default_end_date: Union[str, date], *, date_format: str = '%Y-%m-%d',
194
+ user_groups: Union[Iterable[str], str] = frozenset(), parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
195
+ ) -> None:
196
+ """
197
+ Constructor for DateRangeParameterOption
198
+
199
+ Parameters:
200
+ ...see Attributes of DateRangeParameterOption
201
+ """
202
+ super().__init__(date_format=date_format, user_groups=user_groups, parent_option_ids=parent_option_ids)
203
+ self._default_start_date = self._validate_date(default_start_date)
204
+ self._default_end_date = self._validate_date(default_end_date)
205
+ self._validate_lower_upper_values("default_start_date", self._default_start_date, "default_end_date", self._default_end_date)
134
206
 
135
207
 
136
208
  @dataclass
@@ -138,47 +210,56 @@ class _NumericParameterOption(ParameterOption):
138
210
  """
139
211
  Abstract class (or type) for numeric parameter options
140
212
  """
141
- min_value: Decimal
142
- max_value: Decimal
143
- increment: Decimal
213
+ _min_value: Decimal
214
+ _max_value: Decimal
215
+ _increment: Decimal # = field(default=1, kw_only=True)
144
216
 
145
- def __post_init__(self) -> None:
146
- super().__post_init__()
217
+ @abstractmethod
218
+ def __init__(
219
+ self, min_value: Number, max_value: Number, *, increment: Number = 1, user_groups: Union[Iterable[str], str] = frozenset(),
220
+ parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
221
+ ) -> None:
222
+ super().__init__(user_groups=user_groups, parent_option_ids=parent_option_ids)
147
223
  try:
148
- self.min_value = Decimal(self.min_value)
149
- self.max_value = Decimal(self.max_value)
150
- self.increment = Decimal(self.increment)
224
+ self._min_value = Decimal(min_value)
225
+ self._max_value = Decimal(max_value)
226
+ self._increment = Decimal(increment)
151
227
  except InvalidDecimalConversion as e:
152
228
  raise ConfigurationError(f'Could not convert either min, max, or increment to number') from e
153
229
 
154
- if self.min_value > self.max_value:
155
- raise ConfigurationError(f'The min_value "{self.min_value}" must be less than or equal to ' +
156
- 'the max_value "{self.max_value}"')
157
- if (self.max_value - self.min_value) % self.increment != 0:
158
- raise ConfigurationError(f'The increment "{self.increment}" must fit evenly between ' +
159
- 'the min_value "{self.min_value}" and max_value "{self.max_value}"')
160
-
161
- def _value_in_range(self, value: Decimal, min_value: Decimal) -> bool:
162
- return min_value <= value <= self.max_value
230
+ self._validate_lower_upper_values("min_value", self._min_value, "max_value", self._max_value)
231
+
232
+ if (self._max_value - self._min_value) % self._increment != 0:
233
+ raise ConfigurationError(f'The increment "{self._increment}" must fit evenly between ' +
234
+ f'the min_value "{self._min_value}" and max_value "{self._max_value}"')
235
+
236
+ def __value_in_range(self, value: Decimal) -> bool:
237
+ return self._min_value <= value <= self._max_value
163
238
 
164
- def _value_on_increment(self, value: Decimal, min_value: Decimal) -> bool:
165
- diff = (value - min_value)
166
- return diff >= 0 and diff % self.increment == 0
239
+ def __value_on_increment(self, value: Decimal) -> bool:
240
+ diff = (value - self._min_value)
241
+ return diff >= 0 and diff % self._increment == 0
167
242
 
168
- def _validate_value(self, value: Number, min_value: Optional[Decimal] = None) -> Decimal:
169
- min_value = self.min_value if min_value is None else min_value
243
+ def _validate_value(self, value: Number) -> Decimal:
170
244
  try:
171
245
  value = Decimal(value)
172
246
  except InvalidDecimalConversion as e:
173
247
  raise ConfigurationError(f'Could not convert "{value}" to number', e)
174
248
 
175
- if not self._value_in_range(value, min_value):
249
+ if not self.__value_in_range(value):
176
250
  raise ConfigurationError(f'The selected value "{value}" is outside of bounds ' +
177
251
  '"{min_value}" and "{self.max_value}"')
178
- if not self._value_on_increment(value, min_value):
252
+ if not self.__value_on_increment(value):
179
253
  raise ConfigurationError(f'The difference between selected value "{value}" and lower value ' +
180
254
  '"{min_value}" must be a multiple of increment "{self.increment}"')
181
255
  return value
256
+
257
+ def _to_json_dict(self):
258
+ return {
259
+ "min_value": str(self._min_value),
260
+ "max_value": str(self._max_value),
261
+ "increment": str(self._increment)
262
+ }
182
263
 
183
264
 
184
265
  @dataclass
@@ -191,20 +272,29 @@ class NumberParameterOption(_NumericParameterOption):
191
272
  max_value: Maximum selectable value
192
273
  increment: Increment of selectable values, and must fit evenly between min_value and max_value
193
274
  default_value: Default value for this option, and must be selectable based on min_value, max_value, and increment
194
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
195
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None)
275
+ user_groups: The user groups this parameter option would show for if "user_group_attr" is specified in the Parameter object
276
+ parent_option_ids: Set of parent option ids this parameter option would show for if "parent" is specified in the Parameter object
196
277
  """
197
- default_value: Decimal
198
- parent_option_id: Optional[str] = field(default=None, repr=False)
199
- parent_option_ids: Iterable[str] = frozenset()
278
+ _default_value: Decimal # = field(default=None, kw_only=True)
279
+
280
+ def __init__(
281
+ self, min_value: Number, max_value: Number, *, increment: Number = 1, default_value: Optional[Number] = None,
282
+ user_groups: Union[Iterable[str], str] = frozenset(), parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
283
+ ) -> None:
284
+ """
285
+ Constructor for NumberParameterOption
286
+
287
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
200
288
 
201
- def __post_init__(self) -> None:
202
- super().__post_init__()
203
- self.default_value = self._validate_value(self.default_value)
289
+ Parameters:
290
+ ...see Attributes of NumberParameterOption
291
+ """
292
+ super().__init__(min_value, max_value, increment=increment, user_groups=user_groups, parent_option_ids=parent_option_ids)
293
+ self._default_value = self._validate_value(default_value) if default_value is not None else self._min_value
204
294
 
205
295
 
206
296
  @dataclass
207
- class NumRangeParameterOption(_NumericParameterOption):
297
+ class NumberRangeParameterOption(_NumericParameterOption):
208
298
  """
209
299
  Parameter option for default numeric ranges if it varies based on selection of another parameter
210
300
 
@@ -215,19 +305,26 @@ class NumRangeParameterOption(_NumericParameterOption):
215
305
  default_lower_value: Default lower value for this option, and must be selectable based on min_value, max_value, and increment
216
306
  default_upper_value: Default upper value for this option, and must be selectable based on min_value, max_value, and increment.
217
307
  Must also be greater than default_lower_value
218
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
219
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None)
308
+ user_groups: The user groups this parameter option would show for if "user_group_attr" is specified in the Parameter object
309
+ parent_option_ids: Set of parent option ids this parameter option would show for if "parent" is specified in the Parameter object
220
310
  """
221
- default_lower_value: Decimal
222
- default_upper_value: Decimal
223
- parent_option_id: Optional[str] = field(default=None, repr=False)
224
- parent_option_ids: Iterable[str] = frozenset()
225
-
226
- def __post_init__(self) -> None:
227
- super().__post_init__()
228
- self.default_lower_value = self._validate_value(self.default_lower_value)
229
- self.default_upper_value = self._validate_value(self.default_upper_value, self.default_lower_value)
311
+ _default_lower_value: Decimal # = field(default=None, kw_only=True)
312
+ _default_upper_value: Decimal # = field(default=None, kw_only=True)
230
313
 
314
+ def __init__(
315
+ self, min_value: Number, max_value: Number, *, increment: Number = 1, default_lower_value: Optional[Number] = None,
316
+ default_upper_value: Optional[Number] = None, user_groups: Union[Iterable[str], str] = frozenset(),
317
+ parent_option_ids: Union[Iterable[str], str] = frozenset(), **kwargs
318
+ ) -> None:
319
+ """
320
+ Constructor for NumberRangeParameterOption
321
+
322
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
231
323
 
232
- # Types:
233
- NumericParameterOption = Union[NumberParameterOption, NumRangeParameterOption]
324
+ Parameters:
325
+ ...see Attributes of NumberRangeParameterOption
326
+ """
327
+ super().__init__(min_value, max_value, increment=increment, user_groups=user_groups, parent_option_ids=parent_option_ids)
328
+ self._default_lower_value = self._validate_value(default_lower_value) if default_lower_value is not None else self._min_value
329
+ self._default_upper_value = self._validate_value(default_upper_value) if default_upper_value is not None else self._max_value
330
+ self._validate_lower_upper_values("default_lower_value", self._default_lower_value, "default_upper_value", self._default_upper_value)