mlrl-util 0.12.0__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.
- mlrl/__init__.py +0 -0
- mlrl/util/__init__.py +0 -0
- mlrl/util/arrays.py +169 -0
- mlrl/util/cli.py +340 -0
- mlrl/util/format.py +40 -0
- mlrl/util/options.py +242 -0
- mlrl/util/validation.py +81 -0
- mlrl_util-0.12.0.dist-info/METADATA +36 -0
- mlrl_util-0.12.0.dist-info/RECORD +11 -0
- mlrl_util-0.12.0.dist-info/WHEEL +5 -0
- mlrl_util-0.12.0.dist-info/top_level.txt +1 -0
mlrl/__init__.py
ADDED
|
File without changes
|
mlrl/util/__init__.py
ADDED
|
File without changes
|
mlrl/util/arrays.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Michael Rapp (michael.rapp.ml@gmail.com)
|
|
3
|
+
|
|
4
|
+
Provides utility functions for handling arrays.
|
|
5
|
+
"""
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Optional, Set
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from scipy.sparse import issparse, isspmatrix_coo, isspmatrix_csc, isspmatrix_csr, isspmatrix_dok, isspmatrix_lil, \
|
|
12
|
+
sparray
|
|
13
|
+
|
|
14
|
+
from mlrl.util.format import format_iterable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SparseFormat(Enum):
|
|
18
|
+
"""
|
|
19
|
+
Specifies all valid textual representations of sparse matrix formats.
|
|
20
|
+
"""
|
|
21
|
+
LIL = 'lil'
|
|
22
|
+
COO = 'coo'
|
|
23
|
+
DOK = 'dok'
|
|
24
|
+
CSC = 'csc'
|
|
25
|
+
CSR = 'csr'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_lil(array) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Returns whether a given `scipy.sparse.spmatrix` or `scipy.sparse.sparray` uses the LIL format or not.
|
|
31
|
+
|
|
32
|
+
:param array: A `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
33
|
+
:return: True, if the given array uses the LIL format, False otherwise
|
|
34
|
+
"""
|
|
35
|
+
return isspmatrix_lil(array) or (isinstance(array, sparray) and array.format == 'lil')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def is_coo(array) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Returns whether a given `scipy.sparse.spmatrix` or `scipy.sparse.sparray` uses the COO format or not.
|
|
41
|
+
|
|
42
|
+
:param array: A `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
43
|
+
:return: True, if the given array uses the COO format, False otherwise
|
|
44
|
+
"""
|
|
45
|
+
return isspmatrix_coo(array) or (isinstance(array, sparray) and array.format == 'coo')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_dok(array) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Returns whether a given `scipy.sparse.spmatrix` or `scipy.sparse.sparray` uses the DOK format or not.
|
|
51
|
+
|
|
52
|
+
:param array: A `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
53
|
+
:return: True, if the given array uses the DOK format, False otherwise
|
|
54
|
+
"""
|
|
55
|
+
return isspmatrix_dok(array) or (isinstance(array, sparray) and array.format == 'dok')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_csc(array) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Returns whether a given `scipy.sparse.spmatrix` or `scipy.sparse.sparray` uses the CSC format or not.
|
|
61
|
+
|
|
62
|
+
:param array: A `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
63
|
+
:return: True, if the given array uses the CSC format, False otherwise
|
|
64
|
+
"""
|
|
65
|
+
return isspmatrix_csc(array) or (isinstance(array, sparray) and array.format == 'csc')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_csr(array) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
Returns whether a given `scipy.sparse.spmatrix` or `scipy.sparse.sparray` uses the CSR format or not.
|
|
71
|
+
|
|
72
|
+
:param array: A `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
73
|
+
:return: True, if the given array uses the CSR format, False otherwise
|
|
74
|
+
"""
|
|
75
|
+
return isspmatrix_csr(array) or (isinstance(array, sparray) and array.format == 'csr')
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def is_sparse(array, supported_formats: Optional[Set[SparseFormat]] = None) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
Returns whether a given array is a `scipy.sparse.spmatrix` or `scipy.sparse.sparray` or not.
|
|
81
|
+
|
|
82
|
+
:param array: A `np.ndarray`, `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
83
|
+
:param supported_formats: A set of supported `SparseFormat`s, the `scipy.sparse.spmatrix` or
|
|
84
|
+
`scipy.sparse.sparray` may use or None, if the format should not be checked
|
|
85
|
+
:return: True, if the given array is a `scipy.sparse.spmatrix` or `scipy.sparse.sparray` using
|
|
86
|
+
one of the supported formats, False otherwise
|
|
87
|
+
"""
|
|
88
|
+
if supported_formats:
|
|
89
|
+
lil = SparseFormat.LIL in supported_formats and is_lil(array)
|
|
90
|
+
coo = SparseFormat.COO in supported_formats and is_coo(array)
|
|
91
|
+
dok = SparseFormat.DOK in supported_formats and is_dok(array)
|
|
92
|
+
csc = SparseFormat.CSC in supported_formats and is_csc(array)
|
|
93
|
+
csr = SparseFormat.CSR in supported_formats and is_csr(array)
|
|
94
|
+
|
|
95
|
+
if lil or coo or dok or csc or csr:
|
|
96
|
+
return True
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
return issparse(array)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def is_sparse_and_memory_efficient(array,
|
|
103
|
+
sparse_format: SparseFormat,
|
|
104
|
+
dtype: Optional[np.dtype] = None,
|
|
105
|
+
sparse_values: bool = True) -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Returns whether a given matrix uses sparse format and is expected to occupy less memory than a dense matrix.
|
|
108
|
+
|
|
109
|
+
:param array: A `np.ndarray`, `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be checked
|
|
110
|
+
:param sparse_format: The `SparseFormat` to be used. Must be `SparseFormat.CSC` or `SparseFormat.CSR`
|
|
111
|
+
:param dtype: The type of the values that should be stored in the matrix or None, if it should be obtained
|
|
112
|
+
from the given array
|
|
113
|
+
:param sparse_values: True, if the values must explicitly be stored when using a sparse format, False otherwise
|
|
114
|
+
:return: True, if the given matrix uses a sparse format an is expected to occupy less memory than a
|
|
115
|
+
dense matrix, False otherwise
|
|
116
|
+
"""
|
|
117
|
+
supported_formats = {SparseFormat.CSC, SparseFormat.CSR}
|
|
118
|
+
|
|
119
|
+
if sparse_format not in supported_formats:
|
|
120
|
+
raise ValueError('Unable to estimate memory requirements of given sparse format: Must be one of '
|
|
121
|
+
+ format_iterable(supported_formats) + ', but is "' + str(sparse_format) + '"')
|
|
122
|
+
|
|
123
|
+
if is_sparse(array):
|
|
124
|
+
num_pointers = array.shape[1 if sparse_format == SparseFormat.CSC else 0]
|
|
125
|
+
size_int = np.dtype(np.uint32).itemsize
|
|
126
|
+
dtype = dtype if dtype else array.dtype
|
|
127
|
+
size_data = np.dtype(dtype).itemsize
|
|
128
|
+
size_sparse_data = size_data if sparse_values else 0
|
|
129
|
+
num_dense_elements = array.nnz
|
|
130
|
+
size_sparse = (num_dense_elements * size_sparse_data) + (num_dense_elements * size_int) + (num_pointers
|
|
131
|
+
* size_int)
|
|
132
|
+
size_dense = np.prod(array.shape) * size_data
|
|
133
|
+
return size_sparse < size_dense
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def enforce_dense(array, order: str, dtype: Optional[np.dtype] = None, sparse_value=0) -> np.ndarray:
|
|
138
|
+
"""
|
|
139
|
+
Converts a given array into a `np.ndarray`, if necessary, and enforces a specific memory layout and data type to be
|
|
140
|
+
used.
|
|
141
|
+
|
|
142
|
+
:param array: A `np.ndarray`, `scipy.sparse.spmatrix` or `scipy.sparse.sparray` to be converted
|
|
143
|
+
:param order: The memory layout to be used. Must be `C` or `F`
|
|
144
|
+
:param dtype: The data type to be used or None, if the data type should not be changed
|
|
145
|
+
:param sparse_value: The value that should be used for sparse elements in the given array
|
|
146
|
+
:return: A `np.ndarray` that uses the given memory layout and data type
|
|
147
|
+
"""
|
|
148
|
+
dtype = dtype if dtype else array.dtype
|
|
149
|
+
|
|
150
|
+
if is_sparse(array):
|
|
151
|
+
if sparse_value != 0:
|
|
152
|
+
dense_array = np.full(shape=array.shape, fill_value=sparse_value, dtype=dtype, order=order)
|
|
153
|
+
dense_array[array.nonzero()] = 0
|
|
154
|
+
dense_array += array
|
|
155
|
+
return np.asarray(dense_array, dtype=dtype, order=order)
|
|
156
|
+
return np.require(array.toarray(order=order), dtype=dtype)
|
|
157
|
+
return np.require(array, dtype=dtype, requirements=[order])
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def enforce_2d(array: np.ndarray) -> np.ndarray:
|
|
161
|
+
"""
|
|
162
|
+
Converts a given `np.ndarray` into a two-dimensional array if it is one-dimensional.
|
|
163
|
+
|
|
164
|
+
:param array: A `np.ndarray` to be converted
|
|
165
|
+
:return: A `np.ndarray` with at least two dimensions
|
|
166
|
+
"""
|
|
167
|
+
if array.ndim == 1:
|
|
168
|
+
return np.expand_dims(array, axis=1)
|
|
169
|
+
return array
|
mlrl/util/cli.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Michael Rapp (michael.rapp.ml@gmail.com)
|
|
3
|
+
|
|
4
|
+
Provides classes for configuring the arguments of a command line interface.
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from argparse import ArgumentError, ArgumentParser, Namespace
|
|
9
|
+
from enum import Enum, EnumType
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from typing import Any, Dict, Optional, Set
|
|
12
|
+
|
|
13
|
+
from mlrl.util.format import format_enum_values, format_set
|
|
14
|
+
from mlrl.util.options import BooleanOption, parse_enum, parse_param, parse_param_and_options
|
|
15
|
+
|
|
16
|
+
NONE = 'none'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Argument:
|
|
20
|
+
"""
|
|
21
|
+
A single argument of a command line interface for which the user can provide a custom value.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, *names: str, required: Optional[bool] = False, default: Optional[Any] = None, **kwargs: Any):
|
|
25
|
+
"""
|
|
26
|
+
:param names: One of several names of the argument
|
|
27
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
28
|
+
:param default: The default value of the argument, if any
|
|
29
|
+
:param kwargs: Optional keyword argument to be passed to an `ArgumentParser`
|
|
30
|
+
"""
|
|
31
|
+
self.names = set(names)
|
|
32
|
+
self.required = required
|
|
33
|
+
self.default = default
|
|
34
|
+
self.kwargs = dict(kwargs)
|
|
35
|
+
|
|
36
|
+
@cached_property
|
|
37
|
+
def name(self) -> str:
|
|
38
|
+
"""
|
|
39
|
+
The name of the argument.
|
|
40
|
+
"""
|
|
41
|
+
return next(iter(self.names))
|
|
42
|
+
|
|
43
|
+
@cached_property
|
|
44
|
+
def key(self) -> str:
|
|
45
|
+
"""
|
|
46
|
+
The key of the argument in a `Namespace`.
|
|
47
|
+
"""
|
|
48
|
+
return self.name.lstrip('--').replace('-', '_')
|
|
49
|
+
|
|
50
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
51
|
+
"""
|
|
52
|
+
Returns the value provided by the user for this argument.
|
|
53
|
+
|
|
54
|
+
:param args: A `Namespace` that provides access to the values provided by the user
|
|
55
|
+
:param default: The default value to be returned if no value is available
|
|
56
|
+
:return: The value provided by the user or `default`, if no value is available
|
|
57
|
+
"""
|
|
58
|
+
value = getattr(args, self.key, None)
|
|
59
|
+
value = self.default if value is None else value
|
|
60
|
+
return default if value is None else value
|
|
61
|
+
|
|
62
|
+
def __hash__(self) -> int:
|
|
63
|
+
return hash(self.key)
|
|
64
|
+
|
|
65
|
+
def __eq__(self, other: Any) -> bool:
|
|
66
|
+
return isinstance(other, type(self)) and self.key == other.key
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StringArgument(Argument):
|
|
70
|
+
"""
|
|
71
|
+
An argument of a command line interface for which the user can provide a custom string value.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self,
|
|
75
|
+
*names: str,
|
|
76
|
+
description: Optional[str] = None,
|
|
77
|
+
default: Optional[str] = None,
|
|
78
|
+
required: bool = False):
|
|
79
|
+
"""
|
|
80
|
+
:param names: One or several names of the argument
|
|
81
|
+
:param description: An optional description of the argument
|
|
82
|
+
:param default: The default value
|
|
83
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
84
|
+
"""
|
|
85
|
+
super().__init__(*names, default=default, help=description, type=str, required=required)
|
|
86
|
+
|
|
87
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
88
|
+
value = super().get_value(args, default=default)
|
|
89
|
+
return None if value is None else str(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class IntArgument(Argument):
|
|
93
|
+
"""
|
|
94
|
+
An argument of a command line interface for which the user can provide a custom integer value.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(self,
|
|
98
|
+
*names: str,
|
|
99
|
+
description: Optional[str] = None,
|
|
100
|
+
default: Optional[int] = None,
|
|
101
|
+
required: bool = False):
|
|
102
|
+
"""
|
|
103
|
+
:param names: One or several names of the argument
|
|
104
|
+
:param description: An optional description of the argument
|
|
105
|
+
:param default: The default value
|
|
106
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
107
|
+
"""
|
|
108
|
+
super().__init__(*names, default=default, help=description, type=int, required=required)
|
|
109
|
+
|
|
110
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
111
|
+
value = super().get_value(args, default=default)
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
return None if value is None else int(value)
|
|
115
|
+
except ValueError as error:
|
|
116
|
+
raise ValueError('Expected value of argument ' + self.name + ' to be an integer, but got: '
|
|
117
|
+
+ str(value)) from error
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class FloatArgument(Argument):
|
|
121
|
+
"""
|
|
122
|
+
An argument of a command line interface for which the user can provide a custom floating point value.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(self,
|
|
126
|
+
*names: str,
|
|
127
|
+
description: Optional[str] = None,
|
|
128
|
+
default: Optional[float] = None,
|
|
129
|
+
required: bool = False):
|
|
130
|
+
"""
|
|
131
|
+
:param names: One or several names of the argument
|
|
132
|
+
:param description: An optional description of the argument
|
|
133
|
+
:param default: The default value
|
|
134
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
135
|
+
"""
|
|
136
|
+
super().__init__(*names, default=default, help=description, type=float, required=required)
|
|
137
|
+
|
|
138
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
139
|
+
value = super().get_value(args, default=default)
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
return None if value is None else float(value)
|
|
143
|
+
except ValueError as error:
|
|
144
|
+
raise ValueError('Expected value of argument ' + self.name + ' to be a float, but got: '
|
|
145
|
+
+ str(value)) from error
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class BoolArgument(Argument):
|
|
149
|
+
"""
|
|
150
|
+
An argument of a command line interface for which the user can provide a custom boolean value.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def __format_description(description: str, has_options: bool) -> str:
|
|
155
|
+
if not description.endswith('.'):
|
|
156
|
+
description += '.'
|
|
157
|
+
|
|
158
|
+
description += ' Must be one of ' + format_enum_values(BooleanOption) + '.'
|
|
159
|
+
|
|
160
|
+
if has_options:
|
|
161
|
+
description += ' For additional options refer to the documentation.'
|
|
162
|
+
|
|
163
|
+
return description
|
|
164
|
+
|
|
165
|
+
def __init__(self,
|
|
166
|
+
*names: str,
|
|
167
|
+
description: Optional[str] = None,
|
|
168
|
+
default: Optional[bool] = None,
|
|
169
|
+
required: bool = False,
|
|
170
|
+
true_options: Optional[Set[str]] = None,
|
|
171
|
+
false_options: Optional[Set[str]] = None):
|
|
172
|
+
"""
|
|
173
|
+
:param names: One or several names of the argument
|
|
174
|
+
:param description: An optional description of the argument
|
|
175
|
+
:param default: The default value
|
|
176
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
177
|
+
:param true_options: The names of options that can be provided by the user in addition to the value "true"
|
|
178
|
+
:param false_options: The names of options that can be provided by the user in addition to the value "false"
|
|
179
|
+
"""
|
|
180
|
+
super().__init__(*names,
|
|
181
|
+
default=None if default is None else
|
|
182
|
+
(BooleanOption.TRUE.value if default else BooleanOption.FALSE.value),
|
|
183
|
+
help=self.__format_description(description,
|
|
184
|
+
bool(true_options) or bool(false_options)),
|
|
185
|
+
type=str if true_options or false_options else BooleanOption.parse,
|
|
186
|
+
required=required)
|
|
187
|
+
self.true_options = true_options
|
|
188
|
+
self.false_options = false_options
|
|
189
|
+
|
|
190
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
191
|
+
value = str(super().get_value(args, default=default)).lower()
|
|
192
|
+
|
|
193
|
+
if value:
|
|
194
|
+
true_options = self.true_options
|
|
195
|
+
false_options = self.false_options
|
|
196
|
+
|
|
197
|
+
if true_options or false_options:
|
|
198
|
+
value, options = parse_param_and_options(self.key, value, {
|
|
199
|
+
BooleanOption.TRUE.value: true_options,
|
|
200
|
+
BooleanOption.FALSE.value: false_options
|
|
201
|
+
})
|
|
202
|
+
return BooleanOption.parse(value), options
|
|
203
|
+
|
|
204
|
+
return BooleanOption.parse(value)
|
|
205
|
+
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class SetArgument(Argument):
|
|
210
|
+
"""
|
|
211
|
+
An argument of a command line interface for which the user can provide one out of a predefined set of string values.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def __format_description(description: str, values: Set[str] | Dict[str, Set[str]]) -> str:
|
|
216
|
+
if not description.endswith('.'):
|
|
217
|
+
description += '.'
|
|
218
|
+
|
|
219
|
+
description += ' Must be one of ' + format_set(values.keys() if isinstance(values, dict) else values) + '.'
|
|
220
|
+
|
|
221
|
+
if isinstance(values, dict):
|
|
222
|
+
description += ' For additional options refer to the documentation.'
|
|
223
|
+
|
|
224
|
+
return description
|
|
225
|
+
|
|
226
|
+
def __init__(self,
|
|
227
|
+
*names: str,
|
|
228
|
+
values: Set[str] | Dict[str, Set[str]],
|
|
229
|
+
description: Optional[str] = None,
|
|
230
|
+
default: Optional[str] = None,
|
|
231
|
+
required: bool = False):
|
|
232
|
+
"""
|
|
233
|
+
:param names: One or several names of the argument
|
|
234
|
+
:param values: A set that contains the predefined values or a dictionary that contains the predefined
|
|
235
|
+
values, as well as the names of options that can be provided by the user in addition to the
|
|
236
|
+
respective values
|
|
237
|
+
:param description: An optional description of the argument
|
|
238
|
+
:param default: The default value
|
|
239
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
240
|
+
"""
|
|
241
|
+
super().__init__(*names,
|
|
242
|
+
default=default,
|
|
243
|
+
help=self.__format_description(description, values),
|
|
244
|
+
type=str,
|
|
245
|
+
required=required)
|
|
246
|
+
self.supported_values = values
|
|
247
|
+
|
|
248
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
249
|
+
value = super().get_value(args, default=default)
|
|
250
|
+
|
|
251
|
+
if value:
|
|
252
|
+
supported_values = self.supported_values
|
|
253
|
+
|
|
254
|
+
if isinstance(supported_values, dict):
|
|
255
|
+
return parse_param_and_options(self.key, value, supported_values)
|
|
256
|
+
|
|
257
|
+
return parse_param(self.key, value, supported_values)
|
|
258
|
+
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class EnumArgument(SetArgument):
|
|
263
|
+
"""
|
|
264
|
+
An argument of a command line interface for which the user can provide one out of a predefined set enum values.
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
def __init__(self,
|
|
268
|
+
*names: str,
|
|
269
|
+
enum: EnumType,
|
|
270
|
+
description: Optional[str] = None,
|
|
271
|
+
default: Optional[Enum] = None,
|
|
272
|
+
required: bool = False):
|
|
273
|
+
"""
|
|
274
|
+
:param names: One or several names of the argument
|
|
275
|
+
:param values: An enum that contains the predefined values
|
|
276
|
+
:param description: An optional description of the argument
|
|
277
|
+
:param default: The default value
|
|
278
|
+
:param required: True, if the argument is mandatory, False otherwise
|
|
279
|
+
"""
|
|
280
|
+
super().__init__(
|
|
281
|
+
*names,
|
|
282
|
+
values={x.value if isinstance(x.value, str) else x.name.lower()
|
|
283
|
+
for x in enum},
|
|
284
|
+
description=description,
|
|
285
|
+
default=(default.value if isinstance(default.value, str) else default.name.lower()) if default else None,
|
|
286
|
+
required=required)
|
|
287
|
+
self.enum = enum
|
|
288
|
+
|
|
289
|
+
def get_value(self, args: Namespace, default: Optional[Any] = None) -> Optional[Any]:
|
|
290
|
+
value = super().get_value(args, default=default)
|
|
291
|
+
return parse_enum(self.name, value, self.enum) if value else None
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class CommandLineInterface:
|
|
295
|
+
"""
|
|
296
|
+
Allows to configure a command line interface for running a program.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
def __init__(self, argument_parser: ArgumentParser, version_text: Optional[str] = None):
|
|
300
|
+
"""
|
|
301
|
+
:param argument_parser: The parser that should be used for parsing arguments provided to the command line
|
|
302
|
+
interface by the user
|
|
303
|
+
:param version_text: A text to be shown when the "--version" flag is passed to the command line interface or
|
|
304
|
+
None, if the "--version" flag should not be added to the command line interface
|
|
305
|
+
"""
|
|
306
|
+
self._argument_parser = argument_parser
|
|
307
|
+
|
|
308
|
+
if version_text:
|
|
309
|
+
argument_parser.add_argument('-v',
|
|
310
|
+
'--version',
|
|
311
|
+
action='version',
|
|
312
|
+
version=version_text,
|
|
313
|
+
help='Display information about the program.')
|
|
314
|
+
|
|
315
|
+
def add_arguments(self, *arguments: Argument) -> Optional[Namespace]:
|
|
316
|
+
"""
|
|
317
|
+
Adds a new argument that enables the user to provide a value to the command line interface.
|
|
318
|
+
|
|
319
|
+
:param arguments: The arguments to be added
|
|
320
|
+
"""
|
|
321
|
+
argument_parser = self._argument_parser
|
|
322
|
+
|
|
323
|
+
for argument in arguments:
|
|
324
|
+
try:
|
|
325
|
+
required = argument.required and '--help' not in sys.argv and '-h' not in sys.argv
|
|
326
|
+
argument_parser.add_argument(*argument.names,
|
|
327
|
+
required=required,
|
|
328
|
+
default=argument.default,
|
|
329
|
+
**argument.kwargs)
|
|
330
|
+
except ArgumentError:
|
|
331
|
+
# Argument has already been added
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
def parse_known_args(self) -> Namespace:
|
|
335
|
+
"""
|
|
336
|
+
Parses and returns the values of the arguments already added to the command line interface.
|
|
337
|
+
|
|
338
|
+
:return: A `Namespace` providing access to the values of the arguments
|
|
339
|
+
"""
|
|
340
|
+
return self._argument_parser.parse_known_args()[0]
|
mlrl/util/format.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Michael Rapp (michael.rapp.ml@gmail.com)
|
|
3
|
+
|
|
4
|
+
Provides utility functions for creating textual representations.
|
|
5
|
+
"""
|
|
6
|
+
from enum import EnumType
|
|
7
|
+
from functools import reduce
|
|
8
|
+
from typing import Any, Iterable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def format_iterable(objects: Iterable[Any], separator: str = ', ', delimiter: str = '') -> str:
|
|
12
|
+
"""
|
|
13
|
+
Creates and returns a textual representation of objects in an iterable.
|
|
14
|
+
|
|
15
|
+
:param objects: The iterable of objects to be formatted
|
|
16
|
+
:param separator: The string that should be used as a separator
|
|
17
|
+
:param delimiter: The string that should be added at the beginning and end of each object
|
|
18
|
+
:return: The textual representation that has been created
|
|
19
|
+
"""
|
|
20
|
+
return reduce(lambda aggr, obj: aggr + (separator if aggr else '') + delimiter + str(obj) + delimiter, objects, '')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_enum_values(enum: EnumType) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Creates and returns a textual representation of an enum's values.
|
|
26
|
+
|
|
27
|
+
:param enum: The enum to be formatted
|
|
28
|
+
:return: The textual representation that has been created
|
|
29
|
+
"""
|
|
30
|
+
return format_set({x.value if isinstance(x.value, str) else x.name.lower() for x in enum})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def format_set(objects: Iterable[Any]) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Creates and returns a textual representation of the objects in a set.
|
|
36
|
+
|
|
37
|
+
:param objects: The iterable of objects to be formatted
|
|
38
|
+
:return: The textual representation that has been created
|
|
39
|
+
"""
|
|
40
|
+
return '{' + format_iterable(sorted(objects, key=str), delimiter='"') + '}'
|
mlrl/util/options.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Michael Rapp (michael.rapp.ml@gmail.com)
|
|
3
|
+
|
|
4
|
+
Provides a data structure that allows to store and parse options that are provided as key-value pairs.
|
|
5
|
+
"""
|
|
6
|
+
from enum import Enum, EnumType
|
|
7
|
+
from typing import Any, Dict, Optional, Set, Tuple
|
|
8
|
+
|
|
9
|
+
from mlrl.util.format import format_enum_values, format_set
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BooleanOption(Enum):
|
|
13
|
+
"""
|
|
14
|
+
Specifies all valid textual representations of boolean values.
|
|
15
|
+
"""
|
|
16
|
+
TRUE = 'true'
|
|
17
|
+
FALSE = 'false'
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def parse(text: str) -> bool:
|
|
21
|
+
"""
|
|
22
|
+
Parses a given text that represents a boolean value. If the given text does not represent a valid boolean value,
|
|
23
|
+
a `ValueError` is raised.
|
|
24
|
+
|
|
25
|
+
:param text: The text to be parsed
|
|
26
|
+
:return: True or false, depending on the given text
|
|
27
|
+
"""
|
|
28
|
+
if text == BooleanOption.TRUE.value:
|
|
29
|
+
return True
|
|
30
|
+
if text == BooleanOption.FALSE.value:
|
|
31
|
+
return False
|
|
32
|
+
raise ValueError('Invalid boolean value given. Must be one of ' + format_enum_values(BooleanOption)
|
|
33
|
+
+ ', but is "' + str(text) + '".')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Options:
|
|
37
|
+
"""
|
|
38
|
+
Stores key-value pairs in a dictionary and provides methods to access and validate them.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
ERROR_MESSAGE_INVALID_SYNTAX = 'Invalid syntax used to specify additional options'
|
|
42
|
+
|
|
43
|
+
ERROR_MESSAGE_INVALID_OPTION = 'Expected comma-separated list of key-value pairs'
|
|
44
|
+
|
|
45
|
+
def __init__(self, dictionary: Optional[Dict[str, Any]] = None):
|
|
46
|
+
self.dictionary = dictionary if dictionary is not None else {}
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def create(cls, string: str, allowed_keys: Set[str]):
|
|
50
|
+
"""
|
|
51
|
+
Parses the options that are provided via a given string that is formatted according to the following syntax:
|
|
52
|
+
"[key1=value1,key2=value2]". If the given string is malformed, a `ValueError` will be raised.
|
|
53
|
+
|
|
54
|
+
:param string: The string to be parsed
|
|
55
|
+
:param allowed_keys: A set that contains all valid keys
|
|
56
|
+
:return: An object of type `Options` that stores the key-value pairs that have been parsed from
|
|
57
|
+
the given string
|
|
58
|
+
"""
|
|
59
|
+
options = cls()
|
|
60
|
+
|
|
61
|
+
if string:
|
|
62
|
+
if not string.startswith('{'):
|
|
63
|
+
raise ValueError(Options.ERROR_MESSAGE_INVALID_SYNTAX + '. Must start with "{", but is "' + string
|
|
64
|
+
+ '"')
|
|
65
|
+
if not string.endswith('}'):
|
|
66
|
+
raise ValueError(Options.ERROR_MESSAGE_INVALID_SYNTAX + '. Must end with "}", but is "' + string + '"')
|
|
67
|
+
|
|
68
|
+
string = string[1:-1]
|
|
69
|
+
|
|
70
|
+
if string:
|
|
71
|
+
for option_index, option in enumerate(string.split(',')):
|
|
72
|
+
if option:
|
|
73
|
+
parts = option.split('=')
|
|
74
|
+
|
|
75
|
+
if len(parts) != 2:
|
|
76
|
+
raise ValueError(Options.ERROR_MESSAGE_INVALID_SYNTAX + '. '
|
|
77
|
+
+ Options.ERROR_MESSAGE_INVALID_OPTION + ', but got element "' + option
|
|
78
|
+
+ '" at index ' + str(option_index))
|
|
79
|
+
|
|
80
|
+
key = parts[0]
|
|
81
|
+
|
|
82
|
+
if len(key) == 0:
|
|
83
|
+
raise ValueError(Options.ERROR_MESSAGE_INVALID_SYNTAX + '. '
|
|
84
|
+
+ Options.ERROR_MESSAGE_INVALID_OPTION
|
|
85
|
+
+ ', but key is missing from element "' + option + '" at index '
|
|
86
|
+
+ str(option_index))
|
|
87
|
+
|
|
88
|
+
if key not in allowed_keys:
|
|
89
|
+
raise ValueError('Key must be one of ' + format_set(allowed_keys) + ', but got key "' + key
|
|
90
|
+
+ '" at index ' + str(option_index))
|
|
91
|
+
|
|
92
|
+
value = parts[1]
|
|
93
|
+
|
|
94
|
+
if len(value) == 0:
|
|
95
|
+
raise ValueError(Options.ERROR_MESSAGE_INVALID_SYNTAX + '. '
|
|
96
|
+
+ Options.ERROR_MESSAGE_INVALID_OPTION
|
|
97
|
+
+ ', but value is missing from element "' + option + '" at index '
|
|
98
|
+
+ str(option_index))
|
|
99
|
+
|
|
100
|
+
options.dictionary[key] = value
|
|
101
|
+
|
|
102
|
+
return options
|
|
103
|
+
|
|
104
|
+
def get_string(self, key: str, default_value: Optional[str] = None) -> Optional[str]:
|
|
105
|
+
"""
|
|
106
|
+
Returns a string that corresponds to a specific key.
|
|
107
|
+
|
|
108
|
+
:param key: The key
|
|
109
|
+
:param default_value: The default value to be returned, if no value is associated with the given key
|
|
110
|
+
:return: The value that is associated with the given key or the given default value
|
|
111
|
+
"""
|
|
112
|
+
if key in self.dictionary:
|
|
113
|
+
return str(self.dictionary[key])
|
|
114
|
+
|
|
115
|
+
return default_value
|
|
116
|
+
|
|
117
|
+
def get_bool(self, key: str, default_value: bool) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Returns a boolean that corresponds to a specific key.
|
|
120
|
+
|
|
121
|
+
:param key: The key
|
|
122
|
+
:param default_value: The default value to be returned, if no value is associated with the given key
|
|
123
|
+
:return: The value that is associated with the given key or the given default value
|
|
124
|
+
"""
|
|
125
|
+
if key in self.dictionary:
|
|
126
|
+
value = str(self.dictionary[key])
|
|
127
|
+
return BooleanOption.parse(value)
|
|
128
|
+
|
|
129
|
+
return default_value
|
|
130
|
+
|
|
131
|
+
def get_int(self, key: str, default_value: int) -> int:
|
|
132
|
+
"""
|
|
133
|
+
Returns an integer that corresponds to a specific key.
|
|
134
|
+
|
|
135
|
+
:param key: The key
|
|
136
|
+
:param default_value: The default value to be returned, if no value is associated with the given key
|
|
137
|
+
:return: The value that is associated with the given key or the given default value
|
|
138
|
+
"""
|
|
139
|
+
if key in self.dictionary:
|
|
140
|
+
value = self.dictionary[key]
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
value = int(value)
|
|
144
|
+
except ValueError as error:
|
|
145
|
+
raise ValueError('Value for key "' + key + '" is expected to be an integer, but is "' + str(value)
|
|
146
|
+
+ '"') from error
|
|
147
|
+
|
|
148
|
+
return value
|
|
149
|
+
|
|
150
|
+
return default_value
|
|
151
|
+
|
|
152
|
+
def get_float(self, key: str, default_value: float) -> float:
|
|
153
|
+
"""
|
|
154
|
+
Returns a float that corresponds to a specific key.
|
|
155
|
+
|
|
156
|
+
:param key: The key
|
|
157
|
+
:param default_value: The default value to be returned, if no value is associated with the given key
|
|
158
|
+
:return: The value that is associated with the given key or the given default value
|
|
159
|
+
"""
|
|
160
|
+
if key in self.dictionary:
|
|
161
|
+
value = self.dictionary[key]
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
value = float(value)
|
|
165
|
+
except ValueError as error:
|
|
166
|
+
raise ValueError('Value for key "' + key + '" is expected to be a float, but is "' + str(value)
|
|
167
|
+
+ '"') from error
|
|
168
|
+
|
|
169
|
+
return value
|
|
170
|
+
|
|
171
|
+
return default_value
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def parse_enum(parameter_name: str,
|
|
175
|
+
value: Optional[str],
|
|
176
|
+
enum: EnumType,
|
|
177
|
+
default: Optional[Enum] = None) -> Optional[EnumType]:
|
|
178
|
+
"""
|
|
179
|
+
Parses and returns an enum value. If the given value is invalid, a `ValueError` is raised.
|
|
180
|
+
|
|
181
|
+
:param parameter_name: The name of the parameter
|
|
182
|
+
:param value: The value to be parsed
|
|
183
|
+
:param enum: The enum
|
|
184
|
+
:param default: The default value to be returned if `value` is None
|
|
185
|
+
:return: The value that has been parsed or `default`, if the given value is None
|
|
186
|
+
"""
|
|
187
|
+
if value:
|
|
188
|
+
for enum_value in enum:
|
|
189
|
+
expected_value = enum_value.value if isinstance(enum_value.value, str) else enum_value.name.lower()
|
|
190
|
+
|
|
191
|
+
if expected_value == value:
|
|
192
|
+
return enum_value
|
|
193
|
+
|
|
194
|
+
raise ValueError('Invalid value given for parameter "' + parameter_name + '": Must be one of '
|
|
195
|
+
+ format_enum_values(enum) + ', but is "' + str(value) + '"')
|
|
196
|
+
|
|
197
|
+
return default
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def parse_param(parameter_name: str, value: str, allowed_values: Set[str]) -> str:
|
|
201
|
+
"""
|
|
202
|
+
Parses and returns a parameter value. If the given value is invalid, a `ValueError` is raised.
|
|
203
|
+
|
|
204
|
+
:param parameter_name: The name of the parameter
|
|
205
|
+
:param value: The value to be parsed
|
|
206
|
+
:param allowed_values: A set that contains all valid values
|
|
207
|
+
:return: The value that has been parsed
|
|
208
|
+
"""
|
|
209
|
+
if value in allowed_values:
|
|
210
|
+
return value
|
|
211
|
+
|
|
212
|
+
raise ValueError('Invalid value given for parameter "' + parameter_name + '": Must be one of '
|
|
213
|
+
+ format_set(allowed_values) + ', but is "' + str(value) + '"')
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def parse_param_and_options(parameter_name: str, value: str,
|
|
217
|
+
allowed_values_and_options: Dict[str, Set[str]]) -> Tuple[str, Options]:
|
|
218
|
+
"""
|
|
219
|
+
Parses and returns a parameter value, as well as additional `Options` that may be associated with it. If the given
|
|
220
|
+
value is invalid, a `ValueError` is raised.
|
|
221
|
+
|
|
222
|
+
:param parameter_name: The name of the parameter
|
|
223
|
+
:param value: The value to be parsed
|
|
224
|
+
:param allowed_values_and_options: A dictionary that contains all valid values, as well as their options
|
|
225
|
+
:return: A tuple that contains the value that has been parsed, as well as additional
|
|
226
|
+
`Options`.
|
|
227
|
+
"""
|
|
228
|
+
for allowed_value, allowed_options in allowed_values_and_options.items():
|
|
229
|
+
if value.startswith(allowed_value):
|
|
230
|
+
suffix = value[len(allowed_value):].strip()
|
|
231
|
+
|
|
232
|
+
if suffix:
|
|
233
|
+
try:
|
|
234
|
+
return allowed_value, Options.create(suffix, allowed_options)
|
|
235
|
+
except ValueError as error:
|
|
236
|
+
raise ValueError('Invalid options specified for parameter "' + parameter_name + '" with value "'
|
|
237
|
+
+ allowed_value + '": ' + str(error)) from error
|
|
238
|
+
|
|
239
|
+
return allowed_value, Options()
|
|
240
|
+
|
|
241
|
+
raise ValueError('Invalid value given for parameter "' + parameter_name + '": Must be one of '
|
|
242
|
+
+ format_set(allowed_values_and_options.keys()) + ', but is "' + value + '"')
|
mlrl/util/validation.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Michael Rapp (michael.rapp.ml@gmail.com)
|
|
3
|
+
|
|
4
|
+
Provides utility functions for validation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def assert_greater(name: str, value, threshold):
|
|
9
|
+
"""
|
|
10
|
+
Raises a `ValueError` if a given value is not greater than a specific threshold.
|
|
11
|
+
|
|
12
|
+
:param name: The name of the parameter, the value corresponds to
|
|
13
|
+
:param value: The value
|
|
14
|
+
:param threshold: The threshold
|
|
15
|
+
"""
|
|
16
|
+
if value <= threshold:
|
|
17
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must be greater than ' + str(threshold)
|
|
18
|
+
+ ', but is ' + str(value))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def assert_greater_or_equal(name: str, value, threshold):
|
|
22
|
+
"""
|
|
23
|
+
Raises a `ValueError` if a given value is not greater or equal to a specific threshold.
|
|
24
|
+
|
|
25
|
+
:param name: The name of the parameter, the value corresponds to
|
|
26
|
+
:param value: The value
|
|
27
|
+
:param threshold: The threshold
|
|
28
|
+
"""
|
|
29
|
+
if value < threshold:
|
|
30
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must be greater or equal to '
|
|
31
|
+
+ str(threshold) + ', but is ' + str(value))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def assert_less(name: str, value, threshold):
|
|
35
|
+
"""
|
|
36
|
+
Raises a `ValueError` if a given value is not less than a specific threshold.
|
|
37
|
+
|
|
38
|
+
:param name: The name of the parameter, the value corresponds to
|
|
39
|
+
:param value: The value
|
|
40
|
+
:param threshold: The threshold
|
|
41
|
+
"""
|
|
42
|
+
if value >= threshold:
|
|
43
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must be less than ' + str(threshold)
|
|
44
|
+
+ ', but is ' + str(value))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def assert_less_or_equal(name: str, value, threshold):
|
|
48
|
+
"""
|
|
49
|
+
Raises a `ValueError` if a given value is not less or equal to a specific threshold.
|
|
50
|
+
|
|
51
|
+
:param name: The name of the parameter, the value corresponds to
|
|
52
|
+
:param value: The value
|
|
53
|
+
:param threshold: The threshold
|
|
54
|
+
"""
|
|
55
|
+
if value > threshold:
|
|
56
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must be less or equal to ' + str(threshold)
|
|
57
|
+
+ ', but is ' + str(value))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def assert_multiple(name: str, value, other):
|
|
61
|
+
"""
|
|
62
|
+
Raises a `ValueError` if a given value is not a multiple of another value.
|
|
63
|
+
|
|
64
|
+
:param name: The name of the parameter, the value corresponds to
|
|
65
|
+
:param value: The value that should be a multiple of `other`
|
|
66
|
+
:param other: The other value
|
|
67
|
+
"""
|
|
68
|
+
if value % other != 0:
|
|
69
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must be a multiple of ' + str(other)
|
|
70
|
+
+ ', but is ' + str(value))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def assert_not_none(name: str, value):
|
|
74
|
+
"""
|
|
75
|
+
Raises a `ValueError` if a given value is None.
|
|
76
|
+
|
|
77
|
+
:param name: The name of the parameter, the value corresponds to
|
|
78
|
+
:param value: The value
|
|
79
|
+
"""
|
|
80
|
+
if value is None:
|
|
81
|
+
raise ValueError('Invalid value given for parameter "' + name + '": Must not be None')
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mlrl-util
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: Provides common utilities used by the mlrl-* packages.
|
|
5
|
+
Author-email: Michael Rapp <michael.rapp.ml@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/mrapp-ke/MLRL-Boomer
|
|
8
|
+
Project-URL: source, https://github.com/mrapp-ke/MLRL-Boomer.git
|
|
9
|
+
Project-URL: download, https://github.com/mrapp-ke/MLRL-Boomer/releases
|
|
10
|
+
Project-URL: changelog, https://raw.githubusercontent.com/mrapp-ke/MLRL-Boomer/refs/heads/main/CHANGELOG.md
|
|
11
|
+
Project-URL: documentation, https://mlrl-boomer.readthedocs.io/en/latest
|
|
12
|
+
Project-URL: issues, https://github.com/mrapp-ke/MLRL-Boomer/issues
|
|
13
|
+
Keywords: utilities
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: numpy<2.4,>=2.3
|
|
22
|
+
Requires-Dist: scipy<1.17,>=1.16
|
|
23
|
+
|
|
24
|
+
# "MLRL-Util"
|
|
25
|
+
|
|
26
|
+
[](https://opensource.org/licenses/MIT) [](https://badge.fury.io/py/mlrl-util) [](https://mlrl-boomer.readthedocs.io/en/latest/?badge=latest)
|
|
27
|
+
|
|
28
|
+
**:link: Important links:** [API Reference](https://mlrl-boomer.readthedocs.io/en/latest/developer_guide/api/python/util/mlrl.util.html) | [Issue Tracker](https://github.com/mrapp-ke/MLRL-Boomer/issues) | [Changelog](https://mlrl-boomer.readthedocs.io/en/latest/misc/CHANGELOG.html) | [Contributors](https://mlrl-boomer.readthedocs.io/en/latest/misc/CONTRIBUTORS.html) | [Code of Conduct](https://mlrl-boomer.readthedocs.io/en/latest/misc/CODE_OF_CONDUCT.html) | [License](https://mlrl-boomer.readthedocs.io/en/latest/misc/LICENSE.html)
|
|
29
|
+
|
|
30
|
+
This software package provides common utilities used by the packages [mlrl-common](https://pypi.org/project/mlrl-common/) and [mlrl-testbed](https://pypi.org/project/mlrl-testbed/). It is not of much use on its own.
|
|
31
|
+
|
|
32
|
+
## :scroll: License
|
|
33
|
+
|
|
34
|
+
This project is open source software licensed under the terms of the [MIT license](https://mlrl-boomer.readthedocs.io/en/latest/misc/LICENSE.html). We welcome contributions to the project to enhance its functionality and make it more accessible to a broader audience. A frequently updated list of contributors is available [here](https://mlrl-boomer.readthedocs.io/en/latest/misc/CONTRIBUTORS.html).
|
|
35
|
+
|
|
36
|
+
All contributions to the project and discussions on the [issue tracker](https://github.com/mrapp-ke/MLRL-Boomer/issues) are expected to follow the [code of conduct](https://mlrl-boomer.readthedocs.io/en/latest/misc/CODE_OF_CONDUCT.html).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
mlrl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
mlrl/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
mlrl/util/arrays.py,sha256=yGhiDCBfdgmX7SpaZtWgnLd3929o9qTFpGGpV2UWXhQ,7450
|
|
4
|
+
mlrl/util/cli.py,sha256=lIc5oJ6-VQrE6us7qA_7amkrL9Dwcd4TnbFTNOgs_e4,13777
|
|
5
|
+
mlrl/util/format.py,sha256=inBiNMYPEke3BlItHicZRC_NYb2r_jgQ1lF0SmxRyn8,1519
|
|
6
|
+
mlrl/util/options.py,sha256=mWTWGQZgw1AyKNJg4BFMpb32v9Q4fVfuv7i1BMC96yc,10303
|
|
7
|
+
mlrl/util/validation.py,sha256=mkYKbwrrb5zIig0sIX3JnT_baWPN1WvPwe9R8rCWyWA,2962
|
|
8
|
+
mlrl_util-0.12.0.dist-info/METADATA,sha256=HlCWNH6tNraQWoCC2LLv4j-tgf35fDO9Y3Mr82yXiJg,2856
|
|
9
|
+
mlrl_util-0.12.0.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
|
10
|
+
mlrl_util-0.12.0.dist-info/top_level.txt,sha256=o9x5YHV4Syk8twE0D3Or7SqyG69Ryp7e-HEYe8aB4j4,5
|
|
11
|
+
mlrl_util-0.12.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mlrl
|