syd 0.1.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.
- syd/__init__.py +1 -0
- syd/interactive_viewer.py +213 -0
- syd/notebook_deploy.py +270 -0
- syd/parameters.py +269 -0
- syd-0.1.0.dist-info/METADATA +33 -0
- syd-0.1.0.dist-info/RECORD +8 -0
- syd-0.1.0.dist-info/WHEEL +4 -0
- syd-0.1.0.dist-info/licenses/LICENSE +674 -0
syd/parameters.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from typing import List, Any, Tuple, Generic, TypeVar, cast
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from warnings import warn
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Parameter(Generic[T], ABC):
|
|
12
|
+
"""Abstract base class for parameters that should not be instantiated directly."""
|
|
13
|
+
|
|
14
|
+
name: str
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def __init__(self, name: str, default: T):
|
|
18
|
+
raise NotImplementedError("Need to define in subclass for proper IDE support")
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def value(self) -> T:
|
|
22
|
+
return self._value
|
|
23
|
+
|
|
24
|
+
@value.setter
|
|
25
|
+
def value(self, new_value: T):
|
|
26
|
+
self._value = self._validate(new_value)
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def _validate(self, new_value: Any) -> T:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(init=False)
|
|
34
|
+
class TextParameter(Parameter[str]):
|
|
35
|
+
def __init__(self, name: str, default: str):
|
|
36
|
+
self.name = name
|
|
37
|
+
self.default = default
|
|
38
|
+
self._value = self._validate(default)
|
|
39
|
+
|
|
40
|
+
def _validate(self, new_value: Any) -> str:
|
|
41
|
+
return str(new_value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(init=False)
|
|
45
|
+
class SingleSelectionParameter(Parameter[Any]):
|
|
46
|
+
options: List[Any]
|
|
47
|
+
|
|
48
|
+
def __init__(self, name: str, options: List[Any], default: Any = None):
|
|
49
|
+
self.name = name
|
|
50
|
+
self.options = options
|
|
51
|
+
self.default = default or options[0]
|
|
52
|
+
self._value = self._validate(self.default)
|
|
53
|
+
|
|
54
|
+
def _validate(self, new_value: Any) -> Any:
|
|
55
|
+
if new_value not in self.options:
|
|
56
|
+
raise ValueError(f"Value {new_value} not in options: {self.options}")
|
|
57
|
+
return new_value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(init=False)
|
|
61
|
+
class MultipleSelectionParameter(Parameter[List[Any]]):
|
|
62
|
+
options: List[Any]
|
|
63
|
+
|
|
64
|
+
def __init__(self, name: str, options: List[Any], default: List[Any] = None):
|
|
65
|
+
self.name = name
|
|
66
|
+
self.default = default or []
|
|
67
|
+
self.options = options
|
|
68
|
+
self._value = self._validate(self.default)
|
|
69
|
+
|
|
70
|
+
def _validate(self, new_value: List[Any]) -> List[Any]:
|
|
71
|
+
if not isinstance(new_value, (list, tuple)):
|
|
72
|
+
raise TypeError(f"Expected list or tuple, got {type(new_value)}")
|
|
73
|
+
if not all(val in self.options for val in new_value):
|
|
74
|
+
invalid = [val for val in new_value if val not in self.options]
|
|
75
|
+
raise ValueError(f"Values {invalid} not in options: {self.options}")
|
|
76
|
+
return list(new_value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(init=False)
|
|
80
|
+
class BooleanParameter(Parameter[bool]):
|
|
81
|
+
def __init__(self, name: str, default: bool = True):
|
|
82
|
+
self.name = name
|
|
83
|
+
self.default = default
|
|
84
|
+
self._value = self._validate(default)
|
|
85
|
+
|
|
86
|
+
def _validate(self, new_value: Any) -> bool:
|
|
87
|
+
return bool(new_value)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(init=False)
|
|
91
|
+
class NumericParameter(Parameter[T], ABC):
|
|
92
|
+
min_value: T
|
|
93
|
+
max_value: T
|
|
94
|
+
|
|
95
|
+
def __init__(self, name: str, min_value: T = None, max_value: T = None, default: T = 0):
|
|
96
|
+
self.name = name
|
|
97
|
+
self.default = default
|
|
98
|
+
self.min_value = min_value
|
|
99
|
+
self.max_value = max_value
|
|
100
|
+
self._value = self._validate(default)
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def _validate(self, new_value: Any) -> T:
|
|
104
|
+
# Subclasses must implement this
|
|
105
|
+
raise NotImplementedError
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(init=False)
|
|
109
|
+
class IntegerParameter(NumericParameter[int]):
|
|
110
|
+
def __init__(self, name: str, min_value: int = None, max_value: int = None, default: int = 0):
|
|
111
|
+
self.name = name
|
|
112
|
+
try:
|
|
113
|
+
self.min_value = int(min_value)
|
|
114
|
+
self.max_value = int(max_value)
|
|
115
|
+
except TypeError as e:
|
|
116
|
+
raise TypeError(f"Cannot convert {min_value} and {max_value} to integer") from e
|
|
117
|
+
if self.min_value is not None and self.max_value is not None:
|
|
118
|
+
if self.min_value > self.max_value:
|
|
119
|
+
raise ValueError(f"Minimum value {self.min_value} is greater than maximum value {self.max_value}")
|
|
120
|
+
valid_default = self._validate(default)
|
|
121
|
+
if valid_default != default:
|
|
122
|
+
warn(f"Default value {default} is not in the range [{self.min_value}, {self.max_value}]. Clamping to {valid_default}.")
|
|
123
|
+
self.default = valid_default
|
|
124
|
+
self._value = self._validate(self.default)
|
|
125
|
+
|
|
126
|
+
def _validate(self, new_value: Any) -> int:
|
|
127
|
+
try:
|
|
128
|
+
value = int(new_value)
|
|
129
|
+
except (TypeError, ValueError):
|
|
130
|
+
raise TypeError(f"Cannot convert {new_value} to integer")
|
|
131
|
+
|
|
132
|
+
if self.min_value is not None:
|
|
133
|
+
value = max(self.min_value, value)
|
|
134
|
+
if self.max_value is not None:
|
|
135
|
+
value = min(self.max_value, value)
|
|
136
|
+
return value
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(init=False)
|
|
140
|
+
class FloatParameter(NumericParameter[float]):
|
|
141
|
+
step: float
|
|
142
|
+
|
|
143
|
+
def __init__(self, name: str, min_value: float = None, max_value: float = None, default: float = 0.0, step: float = 0.1):
|
|
144
|
+
self.name = name
|
|
145
|
+
self.default = default
|
|
146
|
+
try:
|
|
147
|
+
self.min_value = float(min_value)
|
|
148
|
+
self.max_value = float(max_value)
|
|
149
|
+
except TypeError as e:
|
|
150
|
+
raise TypeError(f"Cannot convert {min_value} and {max_value} to float") from e
|
|
151
|
+
if self.min_value is not None and self.max_value is not None:
|
|
152
|
+
if self.min_value > self.max_value:
|
|
153
|
+
raise ValueError(f"Minimum value {self.min_value} is greater than maximum value {self.max_value}")
|
|
154
|
+
self.step = step
|
|
155
|
+
valid_default = self._validate(default)
|
|
156
|
+
if valid_default != default:
|
|
157
|
+
warn(f"Default value {default} is not in the range [{self.min_value}, {self.max_value}]. Clamping to {valid_default}.")
|
|
158
|
+
self.default = valid_default
|
|
159
|
+
self._value = self._validate(self.default)
|
|
160
|
+
|
|
161
|
+
def _validate(self, new_value: Any) -> float:
|
|
162
|
+
try:
|
|
163
|
+
value = float(new_value)
|
|
164
|
+
except (TypeError, ValueError):
|
|
165
|
+
raise TypeError(f"Cannot convert {new_value} to float")
|
|
166
|
+
|
|
167
|
+
if self.min_value is not None:
|
|
168
|
+
value = max(self.min_value, value)
|
|
169
|
+
if self.max_value is not None:
|
|
170
|
+
value = min(self.max_value, value)
|
|
171
|
+
return value
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@dataclass(init=False)
|
|
175
|
+
class PairParameter(Parameter[Tuple[T, T]], ABC):
|
|
176
|
+
min_value: T
|
|
177
|
+
max_value: T
|
|
178
|
+
default: Tuple[T, T]
|
|
179
|
+
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def __init__(self, name: str, default: Tuple[T, T], min_value: T = None, max_value: T = None):
|
|
182
|
+
raise NotImplementedError("Need to define in subclass for proper IDE support")
|
|
183
|
+
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def _validate_value(self, value: Any) -> T:
|
|
186
|
+
raise NotImplementedError
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass(init=False)
|
|
190
|
+
class IntegerPairParameter(PairParameter[int]):
|
|
191
|
+
def __init__(self, name: str, default: Tuple[int, int], min_value: int = None, max_value: int = None):
|
|
192
|
+
self.name = name
|
|
193
|
+
try:
|
|
194
|
+
self.min_value = int(min_value)
|
|
195
|
+
self.max_value = int(max_value)
|
|
196
|
+
except TypeError as e:
|
|
197
|
+
raise TypeError(f"Cannot convert {min_value} and {max_value} to integer") from e
|
|
198
|
+
if self.min_value is not None and self.max_value is not None:
|
|
199
|
+
if self.min_value > self.max_value:
|
|
200
|
+
raise ValueError(f"Minimum value {self.min_value} is greater than maximum value {self.max_value}")
|
|
201
|
+
valid_default = self._validate(default)
|
|
202
|
+
if valid_default != default:
|
|
203
|
+
warn(f"Default value {default} is not in the range [{self.min_value}, {self.max_value}]. Clamping to {valid_default}.")
|
|
204
|
+
self.default = valid_default
|
|
205
|
+
self._value = self._validate(self.default)
|
|
206
|
+
|
|
207
|
+
def _validate(self, new_value: Tuple[Any, Any]) -> Tuple[int, int]:
|
|
208
|
+
try:
|
|
209
|
+
values = (int(new_value[0]), int(new_value[1]))
|
|
210
|
+
except (TypeError, ValueError):
|
|
211
|
+
raise TypeError(f"Cannot convert {new_value} to integer pair")
|
|
212
|
+
|
|
213
|
+
if self.min_value is not None:
|
|
214
|
+
values = (max(self.min_value, values[0]), max(self.min_value, values[1]))
|
|
215
|
+
if self.max_value is not None:
|
|
216
|
+
values = (min(self.max_value, values[0]), min(self.max_value, values[1]))
|
|
217
|
+
return values
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@dataclass(init=False)
|
|
221
|
+
class FloatPairParameter(PairParameter[float]):
|
|
222
|
+
step: float
|
|
223
|
+
|
|
224
|
+
def __init__(
|
|
225
|
+
self,
|
|
226
|
+
name: str,
|
|
227
|
+
default: Tuple[float, float],
|
|
228
|
+
min_value: float = None,
|
|
229
|
+
max_value: float = None,
|
|
230
|
+
step: float = 0.1,
|
|
231
|
+
):
|
|
232
|
+
self.name = name
|
|
233
|
+
try:
|
|
234
|
+
self.min_value = float(min_value)
|
|
235
|
+
self.max_value = float(max_value)
|
|
236
|
+
except TypeError as e:
|
|
237
|
+
raise TypeError(f"Cannot convert {min_value} and {max_value} to float") from e
|
|
238
|
+
if self.min_value is not None and self.max_value is not None:
|
|
239
|
+
if self.min_value > self.max_value:
|
|
240
|
+
raise ValueError(f"Minimum value {self.min_value} is greater than maximum value {self.max_value}")
|
|
241
|
+
valid_default = self._validate(default)
|
|
242
|
+
if valid_default != default:
|
|
243
|
+
warn(f"Default value {default} is not in the range [{self.min_value}, {self.max_value}]. Clamping to {valid_default}.")
|
|
244
|
+
self.default = valid_default
|
|
245
|
+
self.step = step
|
|
246
|
+
self._value = self._validate(self.default)
|
|
247
|
+
|
|
248
|
+
def _validate(self, new_value: Tuple[Any, Any]) -> Tuple[float, float]:
|
|
249
|
+
try:
|
|
250
|
+
values = (float(new_value[0]), float(new_value[1]))
|
|
251
|
+
except (TypeError, ValueError):
|
|
252
|
+
raise TypeError(f"Cannot convert {new_value} to float pair")
|
|
253
|
+
|
|
254
|
+
if self.min_value is not None:
|
|
255
|
+
values = (max(self.min_value, values[0]), max(self.min_value, values[1]))
|
|
256
|
+
if self.max_value is not None:
|
|
257
|
+
values = (min(self.max_value, values[0]), min(self.max_value, values[1]))
|
|
258
|
+
return values
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class ParameterType(Enum):
|
|
262
|
+
text = TextParameter
|
|
263
|
+
selection = SingleSelectionParameter
|
|
264
|
+
multiple_selection = MultipleSelectionParameter
|
|
265
|
+
boolean = BooleanParameter
|
|
266
|
+
integer = IntegerParameter
|
|
267
|
+
float = FloatParameter
|
|
268
|
+
integer_pair = IntegerPairParameter
|
|
269
|
+
float_pair = FloatPairParameter
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: syd
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python package for making GUIs for data science easy.
|
|
5
|
+
Project-URL: Homepage, https://github.com/landoskape/syd
|
|
6
|
+
Author-email: Andrew Landau <andrew+tyler+landau+getridofthisanddtheplusses@gmail.com>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: data-science,gui,interactive,jupyter,machine-learning,notebook,python
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
|
|
23
|
+
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# syd
|
|
27
|
+
A package to help you share your data!
|
|
28
|
+
|
|
29
|
+
Have you ever wanted to look through all your data really quickly interactively? Of course you have. Mo data mo problems, but only if you don't know what to do with it. And that starts with looking at your data. And that's why syd stands for show your data!
|
|
30
|
+
|
|
31
|
+
Syd is a system for creating a data viewing GUI that you can view on a web-browser. And guess what? Since it opens on a web browser, you can even open it on any other computer on your local network! For example, your PI. Gone are the days of single random examples that they make infinitely stubborn conclusions about. Now, you can look at all the examples, quickly and easily, on their computer. And that's why syd stands for share your data!
|
|
32
|
+
|
|
33
|
+
Okay, so what is it? Syd is an automated system to convert some basic python plotting code into an interactive GUI. This is great, because it means you only have to think about what you want to plot and what you want to be interactive, syd does the work to make an interface. There's some small overhead for learning how to prepare your data to work with syd, but we provide some templates to make it easy. You know what that means? That means you get to focus on _thinking_ about your data, rather than spending time writing code to look at it. And that's why syd stands for Science, Yes! Datum!
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
syd/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
2
|
+
syd/interactive_viewer.py,sha256=wpTyGw3hK1-p_GbACIri_QaZ2D-CwXPUQ5HTeQsPV-k,9421
|
|
3
|
+
syd/notebook_deploy.py,sha256=rpTDAzYjwjEdMA6DGUFcRHIebE-3hajTIkRa9vKoSL8,11304
|
|
4
|
+
syd/parameters.py,sha256=QMcT4PVSDr6WB4M-f9dbVa1B63RCuPlbMQI0E_JvmKs,9891
|
|
5
|
+
syd-0.1.0.dist-info/METADATA,sha256=5umq2xFIG2wtCxSh8V68i0osShl6MbK796orHWH-P7s,2449
|
|
6
|
+
syd-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
syd-0.1.0.dist-info/licenses/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
|
|
8
|
+
syd-0.1.0.dist-info/RECORD,,
|