modusa 0.2.21__py3-none-any.whl → 0.2.23__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.
Files changed (60) hide show
  1. modusa/decorators.py +4 -4
  2. modusa/devtools/docs/source/generators/audio_waveforms.rst +8 -0
  3. modusa/devtools/docs/source/generators/base.rst +8 -0
  4. modusa/devtools/docs/source/generators/index.rst +8 -0
  5. modusa/devtools/docs/source/io/audio_loader.rst +8 -0
  6. modusa/devtools/docs/source/io/base.rst +8 -0
  7. modusa/devtools/docs/source/io/index.rst +8 -0
  8. modusa/devtools/docs/source/plugins/base.rst +8 -0
  9. modusa/devtools/docs/source/plugins/index.rst +7 -0
  10. modusa/devtools/docs/source/signals/audio_signal.rst +8 -0
  11. modusa/devtools/docs/source/signals/base.rst +8 -0
  12. modusa/devtools/docs/source/signals/frequency_domain_signal.rst +8 -0
  13. modusa/devtools/docs/source/signals/index.rst +11 -0
  14. modusa/devtools/docs/source/signals/spectrogram.rst +8 -0
  15. modusa/devtools/docs/source/signals/time_domain_signal.rst +8 -0
  16. modusa/devtools/docs/source/tools/audio_converter.rst +8 -0
  17. modusa/devtools/docs/source/tools/audio_player.rst +8 -0
  18. modusa/devtools/docs/source/tools/base.rst +8 -0
  19. modusa/devtools/docs/source/tools/fourier_tranform.rst +8 -0
  20. modusa/devtools/docs/source/tools/index.rst +13 -0
  21. modusa/devtools/docs/source/tools/math_ops.rst +8 -0
  22. modusa/devtools/docs/source/tools/plotter.rst +8 -0
  23. modusa/devtools/docs/source/tools/youtube_downloader.rst +8 -0
  24. modusa/devtools/generate_doc_source.py +96 -0
  25. modusa/devtools/generate_template.py +8 -8
  26. modusa/devtools/main.py +3 -2
  27. modusa/devtools/templates/test.py +2 -3
  28. modusa/devtools/templates/{engine.py → tool.py} +3 -8
  29. modusa/generators/__init__.py +0 -2
  30. modusa/generators/audio_waveforms.py +22 -13
  31. modusa/generators/base.py +1 -1
  32. modusa/io/__init__.py +1 -5
  33. modusa/io/audio_loader.py +3 -33
  34. modusa/main.py +0 -30
  35. modusa/signals/__init__.py +1 -5
  36. modusa/signals/audio_signal.py +181 -124
  37. modusa/signals/base.py +1 -8
  38. modusa/signals/frequency_domain_signal.py +140 -93
  39. modusa/signals/spectrogram.py +197 -98
  40. modusa/signals/time_domain_signal.py +177 -74
  41. modusa/tools/__init__.py +2 -0
  42. modusa/{io → tools}/audio_converter.py +12 -4
  43. modusa/tools/audio_player.py +114 -0
  44. modusa/tools/base.py +43 -0
  45. modusa/tools/fourier_tranform.py +24 -0
  46. modusa/tools/math_ops.py +232 -0
  47. modusa/{io → tools}/plotter.py +155 -42
  48. modusa/{io → tools}/youtube_downloader.py +2 -2
  49. modusa/utils/excp.py +9 -42
  50. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/METADATA +2 -1
  51. modusa-0.2.23.dist-info/RECORD +70 -0
  52. modusa/engines/.DS_Store +0 -0
  53. modusa/engines/__init__.py +0 -3
  54. modusa/engines/base.py +0 -14
  55. modusa/io/audio_player.py +0 -72
  56. modusa/signals/signal_ops.py +0 -158
  57. modusa-0.2.21.dist-info/RECORD +0 -47
  58. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/WHEEL +0 -0
  59. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/entry_points.txt +0 -0
  60. {modusa-0.2.21.dist-info → modusa-0.2.23.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from modusa import excp
4
+ from modusa.tools.base import ModusaTool
5
+ from typing import Any
6
+ import numpy as np
7
+
8
+ class MathOps(ModusaTool):
9
+ """
10
+ Performs arithmetic and NumPy-style ops.
11
+
12
+ Note
13
+ ----
14
+ - Shape-changing operations like reshape, transpose, etc. are not yet supported. Use only element-wise or aggregation ops for now.
15
+ - Index alignment must be handled carefully in future extensions.
16
+ """
17
+
18
+ def _axes_match(a1: tuple[np.ndarray, ...], a2: tuple[np.ndarray, ...]) -> bool:
19
+ """
20
+ To check if two axes are same.
21
+
22
+ It checks the length of the axes and the corresponding values.
23
+ """
24
+ if len(a1) != len(a2):
25
+ return False
26
+ return all(np.allclose(x, y, atol=1e-8) for x, y in zip(a1, a2))
27
+
28
+
29
+ #----------------------------------
30
+ # To handle basic element wise
31
+ # math operations like
32
+ # +, -, *, **, / ...
33
+ #----------------------------------
34
+
35
+ @staticmethod
36
+ def add(a: Any, b: Any) -> np.generic | np.ndarray:
37
+ try:
38
+ result = np.add(a, b)
39
+ except Exception as e:
40
+ raise excp.InputError(f"`a` and `b` can't be added") from e
41
+
42
+ if isinstance(a, str) and isinstance(b, str): # numpy actually concatenates, we do not want that
43
+ raise excp.InputError(f"`a` and `b` can't be added")
44
+ return result
45
+
46
+ @staticmethod
47
+ def subtract(a: Any, b: Any) -> np.generic | np.ndarray:
48
+ try:
49
+ result = np.subtract(a, b)
50
+ except Exception as e:
51
+ raise excp.InputError(f"`a` and `b` can't be subtracted") from e
52
+ return result
53
+
54
+ @staticmethod
55
+ def multiply(a: Any, b: Any) -> np.generic | np.ndarray:
56
+ try:
57
+ result = np.multiply(a, b)
58
+ except Exception as e:
59
+ raise excp.InputError(f"`a` and `b` can't be multiplied") from e
60
+
61
+ if isinstance(a, str) and isinstance(b, str): # numpy actually concatenates, we do not want that
62
+ raise excp.InputError(f"`a` and `b` can't be multiplied")
63
+ return result
64
+
65
+ @staticmethod
66
+ def divide(a: Any, b: Any) -> np.generic | np.ndarray:
67
+ try:
68
+ result = np.divide(a, b)
69
+ except Exception as e:
70
+ raise excp.InputError(f"`a` and `b` can't be divided") from e
71
+ return result
72
+
73
+ @staticmethod
74
+ def power(a: Any, b: Any) -> np.generic | np.ndarray:
75
+ try:
76
+ result = np.power(a, b)
77
+ except Exception as e:
78
+ raise excp.InputError(f"`a` can't be exponentiated with `b`") from e
79
+ return result
80
+
81
+ @staticmethod
82
+ def floor_divide(a: Any, b: Any) -> np.generic | np.ndarray:
83
+ try:
84
+ result = np.floor_divide(a, b)
85
+ except Exception as e:
86
+ raise excp.InputError(f"`a` can't be floor divided by `b`") from e
87
+ return result
88
+
89
+ #----------------------------------
90
+ # To handle numpy aggregator ops
91
+ #----------------------------------
92
+ @staticmethod
93
+ def mean(a: Any, axis: int | None = None) -> np.generic | np.ndarray:
94
+ try:
95
+ result = np.mean(a, axis=axis)
96
+ except Exception as e:
97
+ raise excp.InputError(f"can't find mean for `a`") from e
98
+ return result
99
+
100
+ @staticmethod
101
+ def std(a: Any, axis: int | None = None) -> np.generic | np.ndarray:
102
+ """"""
103
+ try:
104
+ result = np.std(a, axis=axis)
105
+ except Exception as e:
106
+ raise excp.InputError(f"can't find std for `a`") from e
107
+ return result
108
+
109
+ @staticmethod
110
+ def min(a: Any, axis: int | None = None) -> np.generic | np.ndarray:
111
+ try:
112
+ result = np.min(a, axis=axis)
113
+ except Exception as e:
114
+ raise excp.InputError(f"can't find min for `a`") from e
115
+ return result
116
+
117
+ @staticmethod
118
+ def max(a: Any, axis: int | None = None) -> np.generic | np.ndarray:
119
+ try:
120
+ result = np.max(a, axis=axis)
121
+ except Exception as e:
122
+ raise excp.InputError(f"can't find max for `a`") from e
123
+ return result
124
+
125
+ @staticmethod
126
+ def sum(a: Any, axis: int | None = None) -> np.generic | np.ndarray:
127
+ try:
128
+ result = np.sum(a, axis=axis)
129
+ except Exception as e:
130
+ raise excp.InputError(f"can't find sum for `a`") from e
131
+ return result
132
+
133
+ #----------------------------------
134
+ # To handle numpy ops where the
135
+ # shapes are unaltered
136
+ # sin, cos, exp, log, ...
137
+ #----------------------------------
138
+
139
+ @staticmethod
140
+ def sin(a: Any) -> np.generic | np.ndarray:
141
+ try:
142
+ result = np.sin(a)
143
+ except Exception as e:
144
+ raise excp.InputError(f"can't find sin for `a`") from e
145
+ return result
146
+
147
+ @staticmethod
148
+ def cos(a: Any) -> np.generic | np.ndarray:
149
+ try:
150
+ result = np.cos(a)
151
+ except Exception as e:
152
+ raise excp.InputError(f"can't find cos for `a`") from e
153
+ return result
154
+
155
+ @staticmethod
156
+ def tanh(a: Any) -> np.generic | np.ndarray:
157
+ try:
158
+ result = np.tanh(a)
159
+ except Exception as e:
160
+ raise excp.InputError(f"can't find tanh for `a`") from e
161
+ return result
162
+
163
+ @staticmethod
164
+ def exp(a: Any) -> np.generic | np.ndarray:
165
+ try:
166
+ result = np.exp(a)
167
+ except Exception as e:
168
+ raise excp.InputError(f"can't find exp for `a`") from e
169
+ return result
170
+
171
+ @staticmethod
172
+ def log(a: Any) -> np.generic | np.ndarray:
173
+ try:
174
+ result = np.log(a)
175
+ except Exception as e:
176
+ raise excp.InputError(f"can't find log for `a`") from e
177
+ return result
178
+
179
+ @staticmethod
180
+ def log10(a: Any) -> np.generic | np.ndarray:
181
+ try:
182
+ result = np.log10(a)
183
+ except Exception as e:
184
+ raise excp.InputError(f"can't find log10 for `a`") from e
185
+ return result
186
+
187
+ @staticmethod
188
+ def log2(a: Any) -> np.generic | np.ndarray:
189
+ try:
190
+ result = np.log2(a)
191
+ except Exception as e:
192
+ raise excp.InputError(f"can't find log2 for `a`") from e
193
+ return result
194
+
195
+ @staticmethod
196
+ def log1p(a: Any) -> np.generic | np.ndarray:
197
+ try:
198
+ result = np.log1p(a)
199
+ except Exception as e:
200
+ raise excp.InputError(f"can't find log1p for `a`") from e
201
+ return result
202
+
203
+
204
+ @staticmethod
205
+ def sqrt(a: Any) -> np.generic | np.ndarray:
206
+ try:
207
+ result = np.sqrt(a)
208
+ except Exception as e:
209
+ raise excp.InputError(f"can't find sqrt for `a`") from e
210
+ return result
211
+
212
+ @staticmethod
213
+ def abs(a: Any) -> np.generic | np.ndarray:
214
+ try:
215
+ result = np.abs(a)
216
+ except Exception as e:
217
+ raise excp.InputError(f"can't find abs for `a`") from e
218
+ return result
219
+
220
+ #------------------------------------
221
+ # TODO: Add shape-changing ops like
222
+ # reshape, transpose, squeeze later
223
+ #------------------------------------
224
+
225
+ @staticmethod
226
+ def reshape(a: Any, shape: int | tuple[int, ...]) -> np.ndarray:
227
+ try:
228
+ result = np.reshape(a, shape=shape)
229
+ except Exception as e:
230
+ raise excp.InputError(f"can't reshape `a`") from e
231
+ return result
232
+
@@ -3,7 +3,7 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.io import ModusaIO
6
+ from modusa.tools.base import ModusaTool
7
7
  import numpy as np
8
8
  import matplotlib.pyplot as plt
9
9
  from matplotlib.patches import Rectangle
@@ -13,7 +13,7 @@ import warnings
13
13
  warnings.filterwarnings("ignore", message="Glyph .* missing from font.*") # To supress any font related warnings, TODO: Add support to Devnagri font
14
14
 
15
15
 
16
- class Plotter(ModusaIO):
16
+ class Plotter(ModusaTool):
17
17
  """
18
18
  Plots different kind of signals using `matplotlib`.
19
19
 
@@ -35,20 +35,20 @@ class Plotter(ModusaIO):
35
35
  def plot_signal(
36
36
  y: np.ndarray,
37
37
  x: np.ndarray | None,
38
- scale_y: tuple[float, float] | None = None,
39
38
  ax: plt.Axes | None = None,
40
- color: str = "k",
41
- marker: str | None = None,
42
- linestyle: str | None = None,
43
- stem: bool = False,
44
- labels: tuple[str, str, str] | None = None,
45
- legend_loc: str | None = None,
39
+ fmt: str = "k",
46
40
  title: str | None = None,
41
+ label: str | None = None,
47
42
  ylabel: str | None = None,
48
43
  xlabel: str | None = None,
49
44
  ylim: tuple[float, float] | None = None,
50
45
  xlim: tuple[float, float] | None = None,
51
46
  highlight: list[tuple[float, float], ...] | None = None,
47
+ vlines: list[float] | None = None,
48
+ hlines: list[float] | None = None,
49
+ show_grid: bool = False,
50
+ stem: bool = False,
51
+ legend_loc: str = None,
52
52
  ) -> plt.Figure | None:
53
53
  """
54
54
  Plots 1D signal using `matplotlib` with various settings passed through the
@@ -67,7 +67,6 @@ class Plotter(ModusaIO):
67
67
  fig = Plotter.plot_signal(
68
68
  y=y,
69
69
  x=x,
70
- scale_y=None,
71
70
  ax=None,
72
71
  color="blue",
73
72
  marker=None,
@@ -86,8 +85,6 @@ class Plotter(ModusaIO):
86
85
  The signal values to plot on the y-axis.
87
86
  x: np.ndarray | None
88
87
  The x-axis values. If None, indices of `y` are used.
89
- scale_y: tuple[float, float] | None
90
- Linear scaling for `y` values, (a, b) => ay+b
91
88
  ax: plt.Axes | None
92
89
  matplotlib Axes object to draw on. If None, a new figure and axis are created. Return type depends on parameter value.
93
90
  color: str
@@ -131,33 +128,31 @@ class Plotter(ModusaIO):
131
128
  if x.shape[0] != y.shape[0]:
132
129
  raise excp.InputValueError(f"`y` and `x` must be of same shape")
133
130
 
134
- # Scale the signal if needed
135
- if scale_y is not None:
136
- if len(scale_y) != 2:
137
- raise excp.InputValueError(f"`scale_y` must be tuple of two values (1, 2) => 1y+2")
138
- a, b = scale_y
139
- y = a * y + b
140
-
141
131
  # Create a figure
142
132
  if ax is None:
143
133
  fig, ax = plt.subplots(figsize=(15, 2))
144
134
  created_fig = True
145
135
  else:
146
136
  fig = ax.get_figure()
147
- created_fig = False
148
-
149
- # Plot the signal with right configurations
150
- plot_label = labels[0] if labels is not None and len(labels) > 0 else None
151
- if stem:
152
- ax.stem(x, y, linefmt=color, markerfmt='o', label=title)
153
- elif marker is not None:
154
- ax.plot(x, y, c=color, linestyle=linestyle, lw=1.5, marker=marker, label=title)
155
- else:
156
- ax.plot(x, y, c=color, linestyle=linestyle, lw=1.5, label=title)
157
-
137
+ created_fig = False
138
+
158
139
  # Add legend
159
- if legend_loc is not None:
140
+ if label is not None:
141
+ legend_loc = legend_loc or "best"
142
+ # Plot the signal and attach the label
143
+ if stem:
144
+ ax.stem(x, y, linefmt="k", markerfmt='o', label=label)
145
+ else:
146
+ ax.plot(x, y, fmt, lw=1.5, ms=3, label=label)
160
147
  ax.legend(loc=legend_loc)
148
+ else:
149
+ # Plot the signal without label
150
+ if stem:
151
+ ax.stem(x, y, linefmt="k", markerfmt='o')
152
+ else:
153
+ ax.plot(x, y, fmt, lw=1.5, ms=3)
154
+
155
+
161
156
 
162
157
  # Set the labels
163
158
  if title is not None:
@@ -173,21 +168,76 @@ class Plotter(ModusaIO):
173
168
  if xlim is not None:
174
169
  ax.set_xlim(xlim)
175
170
 
176
- # Highlight a list of regions
177
171
  if highlight is not None:
178
- for highlight_region in highlight:
172
+ y_min = np.min(y)
173
+ y_max = np.max(y)
174
+ y_range = y_max - y_min
175
+ label_box_height = 0.20 * y_range
176
+
177
+ for i, highlight_region in enumerate(highlight):
179
178
  if len(highlight_region) != 2:
180
- raise excp.InputValueError(f"`highlight should be a list of tuple of 2 values (left, right) => (1, 10.5)")
179
+ raise excp.InputValueError("`highlight` should be a list of tuple of 2 values (left, right) => [(1, 10.5)]")
180
+
181
181
  l, r = highlight_region
182
- ax.add_patch(Rectangle((l, np.min(y)), r - l, np.max(y) - np.min(y), color='red', alpha=0.2, zorder=10))
182
+ l = x[0] if l is None else l
183
+ r = x[-1] if r is None else r
184
+
185
+ # Highlight rectangle (main background)
186
+ ax.add_patch(Rectangle(
187
+ (l, y_min),
188
+ r - l,
189
+ y_range,
190
+ color='red',
191
+ alpha=0.2,
192
+ zorder=10
193
+ ))
194
+
195
+ # Label box inside the top of the highlight
196
+ ax.add_patch(Rectangle(
197
+ (l, y_max - label_box_height),
198
+ r - l,
199
+ label_box_height,
200
+ color='red',
201
+ alpha=0.4,
202
+ zorder=11
203
+ ))
204
+
205
+ # Centered label inside that box
206
+ ax.text(
207
+ (l + r) / 2,
208
+ y_max - label_box_height / 2,
209
+ str(i + 1),
210
+ ha='center',
211
+ va='center',
212
+ fontsize=10,
213
+ color='white',
214
+ fontweight='bold',
215
+ zorder=12
216
+ )
217
+
218
+ # Vertical lines
219
+ if vlines:
220
+ for xpos in vlines:
221
+ ax.axvline(x=xpos, color='blue', linestyle='--', linewidth=2, zorder=5)
222
+
223
+ # Horizontal lines
224
+ if hlines:
225
+ for ypos in hlines:
226
+ ax.axhline(y=ypos, color='blue', linestyle='--', linewidth=2, zorder=5)
227
+
228
+ # Show grid
229
+ if show_grid:
230
+ ax.grid(True, color="gray", linestyle="--", linewidth=0.5)
183
231
 
184
232
  # Show/Return the figure as per needed
185
233
  if created_fig:
186
234
  fig.tight_layout()
187
235
  if Plotter._in_notebook():
236
+ plt.tight_layout()
188
237
  plt.close(fig)
189
238
  return fig
190
239
  else:
240
+ plt.tight_layout()
191
241
  plt.show()
192
242
  return fig
193
243
 
@@ -197,7 +247,6 @@ class Plotter(ModusaIO):
197
247
  M: np.ndarray,
198
248
  r: np.ndarray | None = None,
199
249
  c: np.ndarray | None = None,
200
- log_compression_factor: int | float | None = None,
201
250
  ax: plt.Axes | None = None,
202
251
  cmap: str = "gray_r",
203
252
  title: str | None = None,
@@ -207,7 +256,10 @@ class Plotter(ModusaIO):
207
256
  rlim: tuple[float, float] | None = None,
208
257
  clim: tuple[float, float] | None = None,
209
258
  highlight: list[tuple[float, float, float, float]] | None = None,
259
+ vlines: list[float] | None = None,
260
+ hlines: list[float] | None = None,
210
261
  origin: str = "lower", # or "lower"
262
+ gamma: int | float | None = None,
211
263
  show_colorbar: bool = True,
212
264
  cax: plt.Axes | None = None,
213
265
  show_grid: bool = True,
@@ -307,8 +359,8 @@ class Plotter(ModusaIO):
307
359
  raise excp.InputValueError(f"`c` must have shape as `M column` not {c.shape}")
308
360
 
309
361
  # Scale the signal if needed
310
- if log_compression_factor is not None:
311
- M = np.log1p(float(log_compression_factor) * M)
362
+ if gamma is not None:
363
+ M = np.log1p(float(gamma) * M)
312
364
 
313
365
  # Create a figure
314
366
  if ax is None:
@@ -377,14 +429,65 @@ class Plotter(ModusaIO):
377
429
  if clim is not None:
378
430
  ax.set_xlim(clim)
379
431
 
380
- # Highlight a list of regions
381
432
  if highlight is not None:
382
- for r1, r2, c1, c2 in highlight:
433
+ row_range = r.max() - r.min()
434
+ label_box_height = 0.08 * row_range
435
+
436
+ for i, highlight_region in enumerate(highlight):
437
+ if len(highlight_region) != 4 and len(highlight_region) != 2:
438
+ raise excp.InputValueError(
439
+ "`highlight` should be a list of tuple of 4 or 2 values (row_min, row_max, col_min, col_max) or (col_min, col_max) => [(1, 10.5, 2, 40)] or [(2, 40)] "
440
+ )
441
+
442
+ if len(highlight_region) == 2:
443
+ r1, r2 = None, None
444
+ c1, c2 = highlight_region
445
+ elif len(highlight_region) == 4:
446
+ r1, r2, c1, c2 = highlight_region
447
+
448
+ r1 = r[0] if r1 is None else r1
449
+ r2 = r[-1] if r2 is None else r2
450
+ c1 = c[0] if c1 is None else c1
451
+ c2 = c[-1] if c2 is None else c2
452
+
383
453
  row_min, row_max = min(r1, r2), max(r1, r2)
384
454
  col_min, col_max = min(c1, c2), max(c1, c2)
455
+
385
456
  width = col_max - col_min
386
457
  height = row_max - row_min
387
- ax.add_patch(Rectangle((col_min, row_min), width, height, color='red', alpha=0.2, zorder=10))
458
+
459
+ # Main red highlight box
460
+ ax.add_patch(Rectangle(
461
+ (col_min, row_min),
462
+ width,
463
+ height,
464
+ color='red',
465
+ alpha=0.2,
466
+ zorder=10
467
+ ))
468
+
469
+ # Label box inside top of highlight (just below row_max)
470
+ ax.add_patch(Rectangle(
471
+ (col_min, row_max - label_box_height),
472
+ width,
473
+ label_box_height,
474
+ color='red',
475
+ alpha=0.4,
476
+ zorder=11
477
+ ))
478
+
479
+ # Centered label in that box
480
+ ax.text(
481
+ (col_min + col_max) / 2,
482
+ row_max - (label_box_height / 2),
483
+ str(i + 1),
484
+ ha='center',
485
+ va='center',
486
+ fontsize=10,
487
+ color='white',
488
+ fontweight='bold',
489
+ zorder=12
490
+ )
388
491
 
389
492
  # Show colorbar
390
493
  if show_colorbar is not None:
@@ -392,9 +495,19 @@ class Plotter(ModusaIO):
392
495
  if Mlabel is not None:
393
496
  cbar.set_label(Mlabel)
394
497
 
498
+ # Vertical lines
499
+ if vlines:
500
+ for xpos in vlines:
501
+ ax.axvline(x=xpos, color='blue', linestyle='--', linewidth=2, zorder=5)
502
+
503
+ # Horizontal lines
504
+ if hlines:
505
+ for ypos in hlines:
506
+ ax.axhline(y=ypos, color='blue', linestyle='--', linewidth=2, zorder=5)
507
+
395
508
  # Show grid
396
509
  if show_grid:
397
- ax.grid(True, color="gray", linestyle="--", linewidth=0.5) # TODO
510
+ ax.grid(True, color="gray", linestyle="--", linewidth=0.5)
398
511
 
399
512
  # Show/Return the figure as per needed
400
513
  if created_fig:
@@ -3,12 +3,12 @@
3
3
 
4
4
  from modusa import excp
5
5
  from modusa.decorators import validate_args_type
6
- from modusa.io.base import ModusaIO
6
+ from modusa.tools.base import ModusaTool
7
7
  from typing import Any
8
8
  from pathlib import Path
9
9
  import yt_dlp
10
10
 
11
- class YoutubeDownloader(ModusaIO):
11
+ class YoutubeDownloader(ModusaTool):
12
12
  """
13
13
  Download highest quality audio/video from YouTube.
14
14
 
modusa/utils/excp.py CHANGED
@@ -4,73 +4,40 @@
4
4
  #----------------------------------------
5
5
  # Base class errors
6
6
  #----------------------------------------
7
- class MusaBaseError(Exception):
7
+ class ModusaBaseError(Exception):
8
8
  """
9
9
  Ultimate base class for any kind of custom errors.
10
10
  """
11
11
  pass
12
12
 
13
- class TypeError(MusaBaseError):
13
+ class TypeError(ModusaBaseError):
14
14
  pass
15
15
 
16
- class InputError(MusaBaseError):
16
+ class InputError(ModusaBaseError):
17
17
  """
18
18
  Any Input type error.
19
19
  """
20
20
 
21
- class InputTypeError(MusaBaseError):
21
+ class InputTypeError(ModusaBaseError):
22
22
  """
23
23
  Any Input type error.
24
24
  """
25
25
 
26
- class InputValueError(MusaBaseError):
26
+ class InputValueError(ModusaBaseError):
27
27
  """
28
28
  Any Input type error.
29
29
  """
30
30
 
31
- class ImmutableAttributeError(MusaBaseError):
31
+ class ImmutableAttributeError(ModusaBaseError):
32
32
  """Raised when attempting to modify an immutable attribute."""
33
33
  pass
34
34
 
35
- class FileNotFoundError(MusaBaseError):
35
+ class FileNotFoundError(ModusaBaseError):
36
36
  """Raised when file does not exist."""
37
37
  pass
38
38
 
39
-
40
- class PluginInputError(MusaBaseError):
41
- pass
42
-
43
- class PluginOutputError(MusaBaseError):
39
+ class PluginInputError(ModusaBaseError):
44
40
  pass
45
-
46
-
47
41
 
48
- class SignalOpError(MusaBaseError):
49
- pass
50
-
51
- class AttributeNotFoundError(MusaBaseError):
52
- pass
53
-
54
- class ParsingError(MusaBaseError):
55
- """
56
- Base class for any parsing related issues
57
- """
58
- pass
59
-
60
- class ValidationError(MusaBaseError):
61
- """
62
- Base class for all input validation error
63
- """
64
- pass
65
-
66
- class GenerationError(MusaBaseError):
67
- """
68
- Error when generation fails
69
- """
70
- pass
71
-
72
- class FileLoadingError(MusaBaseError):
73
- """
74
- Error loading a file
75
- """
42
+ class PluginOutputError(ModusaBaseError):
76
43
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modusa
3
- Version: 0.2.21
3
+ Version: 0.2.23
4
4
  Summary: A modular signal analysis python library.
5
5
  Author-Email: Ankit Anand <ankit0.anand0@gmail.com>
6
6
  License: MIT
@@ -9,6 +9,7 @@ Requires-Dist: numpy>=2.2.6
9
9
  Requires-Dist: matplotlib>=3.10.3
10
10
  Requires-Dist: yt-dlp>=2025.6.30
11
11
  Requires-Dist: librosa==0.10.1
12
+ Requires-Dist: IPython>=8.0.0
12
13
  Description-Content-Type: text/markdown
13
14
 
14
15
  # modusa