spectre-core 0.0.22__py3-none-any.whl → 0.0.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 (77) hide show
  1. spectre_core/_file_io/__init__.py +4 -4
  2. spectre_core/_file_io/file_handlers.py +60 -106
  3. spectre_core/batches/__init__.py +20 -3
  4. spectre_core/batches/_base.py +85 -134
  5. spectre_core/batches/_batches.py +55 -99
  6. spectre_core/batches/_factory.py +21 -20
  7. spectre_core/batches/_register.py +8 -8
  8. spectre_core/batches/plugins/_batch_keys.py +7 -6
  9. spectre_core/batches/plugins/_callisto.py +65 -97
  10. spectre_core/batches/plugins/_iq_stream.py +105 -169
  11. spectre_core/capture_configs/__init__.py +46 -17
  12. spectre_core/capture_configs/_capture_config.py +25 -52
  13. spectre_core/capture_configs/_capture_modes.py +8 -6
  14. spectre_core/capture_configs/_capture_templates.py +50 -110
  15. spectre_core/capture_configs/_parameters.py +37 -74
  16. spectre_core/capture_configs/_pconstraints.py +40 -40
  17. spectre_core/capture_configs/_pnames.py +36 -34
  18. spectre_core/capture_configs/_ptemplates.py +260 -347
  19. spectre_core/capture_configs/_pvalidators.py +99 -102
  20. spectre_core/config/__init__.py +13 -8
  21. spectre_core/config/_paths.py +18 -35
  22. spectre_core/config/_time_formats.py +6 -5
  23. spectre_core/exceptions.py +38 -0
  24. spectre_core/jobs/__init__.py +3 -6
  25. spectre_core/jobs/_duration.py +12 -0
  26. spectre_core/jobs/_jobs.py +72 -43
  27. spectre_core/jobs/_workers.py +55 -105
  28. spectre_core/logs/__init__.py +7 -2
  29. spectre_core/logs/_configure.py +13 -17
  30. spectre_core/logs/_decorators.py +6 -4
  31. spectre_core/logs/_logs.py +37 -89
  32. spectre_core/logs/_process_types.py +5 -3
  33. spectre_core/plotting/__init__.py +13 -3
  34. spectre_core/plotting/_base.py +64 -138
  35. spectre_core/plotting/_format.py +10 -8
  36. spectre_core/plotting/_panel_names.py +7 -5
  37. spectre_core/plotting/_panel_stack.py +82 -115
  38. spectre_core/plotting/_panels.py +120 -155
  39. spectre_core/post_processing/__init__.py +6 -3
  40. spectre_core/post_processing/_base.py +41 -55
  41. spectre_core/post_processing/_factory.py +14 -11
  42. spectre_core/post_processing/_post_processor.py +16 -12
  43. spectre_core/post_processing/_register.py +10 -7
  44. spectre_core/post_processing/plugins/_event_handler_keys.py +4 -3
  45. spectre_core/post_processing/plugins/_fixed_center_frequency.py +54 -47
  46. spectre_core/post_processing/plugins/_swept_center_frequency.py +199 -174
  47. spectre_core/receivers/__init__.py +9 -2
  48. spectre_core/receivers/_base.py +82 -148
  49. spectre_core/receivers/_factory.py +20 -30
  50. spectre_core/receivers/_register.py +7 -10
  51. spectre_core/receivers/_spec_names.py +17 -15
  52. spectre_core/receivers/plugins/_b200mini.py +47 -60
  53. spectre_core/receivers/plugins/_receiver_names.py +8 -6
  54. spectre_core/receivers/plugins/_rsp1a.py +44 -40
  55. spectre_core/receivers/plugins/_rspduo.py +59 -44
  56. spectre_core/receivers/plugins/_sdrplay_receiver.py +67 -83
  57. spectre_core/receivers/plugins/_test.py +136 -129
  58. spectre_core/receivers/plugins/_usrp.py +93 -85
  59. spectre_core/receivers/plugins/gr/__init__.py +1 -1
  60. spectre_core/receivers/plugins/gr/_base.py +14 -22
  61. spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
  62. spectre_core/receivers/plugins/gr/_rspduo.py +77 -89
  63. spectre_core/receivers/plugins/gr/_test.py +49 -57
  64. spectre_core/receivers/plugins/gr/_usrp.py +61 -59
  65. spectre_core/spectrograms/__init__.py +21 -13
  66. spectre_core/spectrograms/_analytical.py +108 -99
  67. spectre_core/spectrograms/_array_operations.py +39 -46
  68. spectre_core/spectrograms/_spectrogram.py +289 -322
  69. spectre_core/spectrograms/_transform.py +106 -73
  70. spectre_core/wgetting/__init__.py +1 -3
  71. spectre_core/wgetting/_callisto.py +87 -93
  72. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/METADATA +9 -23
  73. spectre_core-0.0.23.dist-info/RECORD +79 -0
  74. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/WHEEL +1 -1
  75. spectre_core-0.0.22.dist-info/RECORD +0 -78
  76. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/licenses/LICENSE +0 -0
  77. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/top_level.txt +0 -0
@@ -5,9 +5,9 @@
5
5
  """Basic internal file handling capabilities."""
6
6
 
7
7
  from .file_handlers import (
8
- BaseFileHandler, JsonHandler, TextHandler,
8
+ BaseFileHandler,
9
+ JsonHandler,
10
+ TextHandler,
9
11
  )
10
12
 
11
- __all__ = [
12
- "BaseFileHandler", "JsonHandler", "TextHandler"
13
- ]
13
+ __all__ = ["BaseFileHandler", "JsonHandler", "TextHandler"]
@@ -7,14 +7,15 @@ import json
7
7
  from abc import ABC, abstractmethod
8
8
  from typing import Any, Optional, TypeVar, Generic
9
9
 
10
- T = TypeVar('T')
10
+ T = TypeVar("T")
11
+
11
12
 
12
13
  class BaseFileHandler(ABC, Generic[T]):
13
14
  """
14
15
  Base class for handling file operations.
15
16
 
16
- When subclassing, specify the return type of `_read`
17
- using `Generic[T]`.
17
+ When subclassing, specify the return type of `_read`
18
+ using `Generic[T]`.
18
19
 
19
20
  Example:
20
21
  .. code-block:: python
@@ -26,11 +27,9 @@ class BaseFileHandler(ABC, Generic[T]):
26
27
  # Implementation here
27
28
  ...
28
29
  """
30
+
29
31
  def __init__(
30
- self,
31
- parent_dir_path: str,
32
- base_file_name: str,
33
- extension: Optional[str] = None
32
+ self, parent_dir_path: str, base_file_name: str, extension: Optional[str] = None
34
33
  ) -> None:
35
34
  """Initialise a `BaseFileHandler` instance.
36
35
 
@@ -39,130 +38,101 @@ class BaseFileHandler(ABC, Generic[T]):
39
38
  :param extension: The file extension (without the dot), defaults to None
40
39
  """
41
40
  self._data_cache: Optional[T] = None
42
-
41
+
43
42
  self._parent_dir_path = parent_dir_path
44
- self._base_file_name = base_file_name
45
-
43
+ self._base_file_name = base_file_name
44
+
46
45
  if extension == "":
47
46
  extension = None
48
47
  self._extension = extension
49
48
 
50
-
51
49
  @abstractmethod
52
- def _read(
53
- self
54
- ) -> T:
50
+ def _read(self) -> T:
55
51
  """The grunt work to return the file contents.
56
52
 
57
53
  :return: The file contents.
58
54
  """
59
-
60
-
55
+
61
56
  @property
62
- def parent_dir_path(
63
- self
64
- ) -> str:
57
+ def parent_dir_path(self) -> str:
65
58
  """Return the parent directory path for the file."""
66
59
  return self._parent_dir_path
67
-
68
60
 
69
61
  @property
70
- def base_file_name(
71
- self
72
- ) -> str:
62
+ def base_file_name(self) -> str:
73
63
  """Return the file name, stripped of the file extension."""
74
64
  return self._base_file_name
75
-
76
65
 
77
66
  @property
78
- def extension(
79
- self
80
- ) -> Optional[str]:
67
+ def extension(self) -> Optional[str]:
81
68
  """Return the file path suffix, excluding the dot."""
82
69
  return self._extension
83
-
84
70
 
85
71
  @property
86
- def file_name(
87
- self
88
- ) -> str:
72
+ def file_name(self) -> str:
89
73
  """Generate the file name based on the base name and extension.
90
74
 
91
75
  :return: The file name with the extension (including the dot), or the base name if no extension is set.
92
76
  """
93
- return self._base_file_name if (self._extension is None) else f"{self._base_file_name}.{self._extension}"
94
-
77
+ return (
78
+ self._base_file_name
79
+ if (self._extension is None)
80
+ else f"{self._base_file_name}.{self._extension}"
81
+ )
95
82
 
96
83
  @property
97
- def file_path(
98
- self
99
- ) -> str:
84
+ def file_path(self) -> str:
100
85
  """The absolute or relative file path as defined by the parent directory path,
101
86
  base file name and extension."""
102
87
  return os.path.join(self._parent_dir_path, self.file_name)
103
-
104
-
88
+
105
89
  @property
106
- def exists(
107
- self
108
- ) -> bool:
90
+ def exists(self) -> bool:
109
91
  """Check if the file exists in the filesystem."""
110
- return os.path.exists(self.file_path)
111
-
92
+ return os.path.exists(self.file_path)
112
93
 
113
- def read(
114
- self,
115
- cache: bool = True
116
- ) -> T:
94
+ def read(self, cache: bool = True) -> T:
117
95
  """Read the file contents.
118
-
96
+
119
97
  :param cache: If False, bypasses the cache and reads the file directly on each `read` call, defaults to True
120
98
  :return: The file contents.
121
99
  """
122
100
  # if the user has specified to ignore the cache, simply read the file.
123
101
  if not cache:
124
102
  return self._read()
125
-
103
+
126
104
  # otherwise make use of the cache
127
105
  if self._data_cache is None:
128
106
  self._data_cache = self._read()
129
107
  return self._data_cache
130
-
131
-
132
- def make_parent_dir_path(
133
- self
134
- ) -> None:
135
- """Make the parent directory path of the file. No error is raised if the target
108
+
109
+ def make_parent_dir_path(self) -> None:
110
+ """Make the parent directory path of the file. No error is raised if the target
136
111
  directory already exists.
137
112
  """
138
- os.makedirs(self.parent_dir_path, exist_ok=True)
139
-
113
+ os.makedirs(self.parent_dir_path, exist_ok=True)
140
114
 
141
- def delete(
142
- self,
143
- ignore_if_missing: bool = False
144
- ) -> None:
115
+ def delete(self, ignore_if_missing: bool = False) -> None:
145
116
  """Delete the file from the filesystem.
146
117
 
147
118
  :param ignore_if_missing: If True, skips deletion if the file does not exist, defaults to False
148
119
  :raises FileNotFoundError: If the file is missing and `ignore_if_missing` is False.
149
120
  """
150
121
  if not self.exists and not ignore_if_missing:
151
- raise FileNotFoundError(f"{self.file_name} does not exist, and so cannot be deleted")
122
+ raise FileNotFoundError(
123
+ f"{self.file_name} does not exist, and so cannot be deleted"
124
+ )
152
125
  else:
153
126
  os.remove(self.file_path)
154
-
155
127
 
156
- def cat(
157
- self
158
- ) -> None:
128
+ def cat(self) -> None:
159
129
  """Display the file contents on the standard output."""
160
130
  print(self.read())
161
131
 
162
132
 
163
133
  class JsonHandler(BaseFileHandler[dict[str, Any]]):
164
134
  """File handler for JSON formatted files.
165
-
135
+
166
136
  We assume that the files are of the form
167
137
  {
168
138
  "foo": <JSON compatable structure>
@@ -170,29 +140,17 @@ class JsonHandler(BaseFileHandler[dict[str, Any]]):
170
140
  }
171
141
 
172
142
  """
143
+
173
144
  def __init__(
174
- self,
175
- parent_dir_path: str,
176
- base_file_name: str,
177
- extension: str = "json"
145
+ self, parent_dir_path: str, base_file_name: str, extension: str = "json"
178
146
  ) -> None:
179
- super().__init__(parent_dir_path,
180
- base_file_name,
181
- extension)
182
-
183
-
184
- def _read(
185
- self
186
- ) -> dict[str, Any]:
187
- with open(self.file_path, 'r') as f:
147
+ super().__init__(parent_dir_path, base_file_name, extension)
148
+
149
+ def _read(self) -> dict[str, Any]:
150
+ with open(self.file_path, "r") as f:
188
151
  return json.load(f)
189
-
190
-
191
- def save(
192
- self,
193
- d: dict[str, Any],
194
- force: bool = False
195
- ) -> None:
152
+
153
+ def save(self, d: dict[str, Any], force: bool = False) -> None:
196
154
  """Save the input dictionary to file in the JSON file format.
197
155
 
198
156
  :param d: The dictionary to save.
@@ -205,29 +163,25 @@ class JsonHandler(BaseFileHandler[dict[str, Any]]):
205
163
  if force:
206
164
  pass
207
165
  else:
208
- raise FileExistsError((f"{self.file_name} already exists, write has been abandoned. "
209
- f"You can override this functionality with `force`"))
166
+ raise FileExistsError(
167
+ (
168
+ f"{self.file_name} already exists, write has been abandoned. "
169
+ f"You can override this functionality with `force`"
170
+ )
171
+ )
210
172
 
211
- with open(self.file_path, 'w') as file:
212
- json.dump(d, file, indent=4)
173
+ with open(self.file_path, "w") as file:
174
+ json.dump(d, file, indent=4)
213
175
 
214
-
215
176
 
216
177
  class TextHandler(BaseFileHandler[str]):
217
178
  """File handler for text formatted files."""
179
+
218
180
  def __init__(
219
- self,
220
- parent_dir_path: str,
221
- base_file_name: str,
222
- extension: str = "txt"
181
+ self, parent_dir_path: str, base_file_name: str, extension: str = "txt"
223
182
  ) -> None:
224
- super().__init__(parent_dir_path,
225
- base_file_name,
226
- extension)
227
-
228
-
229
- def _read(
230
- self
231
- ) -> str:
232
- with open(self.file_path, 'r') as f:
233
- return f.read()
183
+ super().__init__(parent_dir_path, base_file_name, extension)
184
+
185
+ def _read(self) -> str:
186
+ with open(self.file_path, "r") as f:
187
+ return f.read()
@@ -10,12 +10,29 @@ from .plugins._batch_keys import BatchKey
10
10
  from .plugins._iq_stream import IQStreamBatch, IQMetadata
11
11
  from .plugins._callisto import CallistoBatch
12
12
 
13
- from ._base import BaseBatch, BatchFile, parse_batch_base_file_name
13
+ from ._base import (
14
+ BaseBatch,
15
+ BatchFile,
16
+ parse_batch_file_name,
17
+ parse_batch_base_file_name,
18
+ )
14
19
  from ._batches import Batches
15
20
  from ._factory import get_batch_cls, get_batch_cls_from_tag
16
21
 
17
22
  __all__ = [
18
- "IQStreamBatch", "IQMetadata", "CallistoBatch", "BaseBatch", "BatchFile",
19
- "Batches", "get_batch_cls", "BatchKey", "get_batch_cls_from_tag", "parse_batch_base_file_name"
23
+ "IQStreamBatch",
24
+ "IQMetadata",
25
+ "CallistoBatch",
26
+ "BaseBatch",
27
+ "BatchFile",
28
+ "Batches",
29
+ "get_batch_cls",
30
+ "BatchKey",
31
+ "get_batch_cls_from_tag",
32
+ "parse_batch_file_name",
20
33
  ]
21
34
 
35
+ # To be deprecated.
36
+ __all__ += [
37
+ "parse_batch_base_file_name",
38
+ ]
@@ -13,21 +13,40 @@ from os.path import splitext
13
13
  from spectre_core._file_io import BaseFileHandler
14
14
  from spectre_core.config import get_batches_dir_path, TimeFormat
15
15
  from spectre_core.spectrograms import Spectrogram
16
+ from spectre_core.exceptions import deprecated
16
17
 
17
18
 
18
- def parse_batch_base_file_name(
19
- base_file_name: str
20
- ) -> Tuple[str, str, str]:
19
+ @deprecated(
20
+ "The terminology (base file name) is inconsistent with the `_file_io` submodule. "
21
+ f"Please use `parse_batch_file_name` instead."
22
+ )
23
+ def parse_batch_base_file_name(base_file_name: str) -> Tuple[str, str, str]:
24
+ """Included for backwards compatability - please use `parse_batch_file_name` instead."""
25
+ return parse_batch_file_name(base_file_name)
26
+
27
+
28
+ def parse_batch_file_name(file_name: str) -> Tuple[str, str, str]:
21
29
  """Parse the base file name of a batch file into a start time, tag and extension.
30
+
31
+ :param file_name: The file name of the batch file, with optional extension.
32
+ :return: The file name decomposed into its start time, tag and extension. If no extension is present,
33
+ the final element of the tuple will be an empty string.
22
34
  """
23
- batch_name, extension = splitext(base_file_name)
35
+ batch_name, extension = splitext(file_name)
36
+
37
+ num_underscores = batch_name.count("_")
38
+ if num_underscores != 1:
39
+ raise ValueError(
40
+ f"Expected exactly one underscore in the batch name '{batch_name}'. Found {num_underscores}"
41
+ )
42
+
24
43
  # strip the dot from the extension
25
- extension = extension.lstrip('.')
44
+ extension = extension.lstrip(".")
26
45
  start_time, tag = batch_name.split("_", 1)
27
46
  return start_time, tag, extension
28
47
 
29
48
 
30
- T = TypeVar('T')
49
+ T = TypeVar("T")
31
50
 
32
51
 
33
52
  class BatchFile(BaseFileHandler[T]):
@@ -37,14 +56,12 @@ class BatchFile(BaseFileHandler[T]):
37
56
 
38
57
  `<start time>_<tag>.<extension>`
39
58
 
40
- The substring `<start time>_<tag>` is referred to as the batch name. Files with the same batch name
59
+ The substring `<start time>_<tag>` is referred to as the batch name. Files with the same batch name
41
60
  belong to the same batch.
42
61
  """
62
+
43
63
  def __init__(
44
- self,
45
- batch_parent_dir_path: str,
46
- batch_name: str,
47
- extension: str
64
+ self, batch_parent_dir_path: str, batch_name: str, extension: str
48
65
  ) -> None:
49
66
  """Initialise a `BatchFile` instance.
50
67
 
@@ -52,52 +69,39 @@ class BatchFile(BaseFileHandler[T]):
52
69
  :param batch_name: Base file name, composed of the batch start time and tag.
53
70
  :param extension: File extension.
54
71
  """
55
- super().__init__(batch_parent_dir_path,
56
- batch_name,
57
- extension)
72
+ super().__init__(batch_parent_dir_path, batch_name, extension)
58
73
  self._start_time, self._tag = batch_name.split("_")
59
-
60
-
74
+
61
75
  @property
62
- def start_time(
63
- self
64
- ) -> str:
76
+ def start_time(self) -> str:
65
77
  """The start time of the batch, formatted as a string up to seconds precision."""
66
78
  return self._start_time
67
79
 
68
-
69
80
  @cached_property
70
- def start_datetime(
71
- self
72
- ) -> datetime:
81
+ def start_datetime(self) -> datetime:
73
82
  """The start time of the batch, parsed as a datetime up to seconds precision."""
74
83
  return datetime.strptime(self.start_time, TimeFormat.DATETIME)
75
84
 
76
-
77
85
  @property
78
- def tag(
79
- self
80
- ) -> str:
86
+ def tag(self) -> str:
81
87
  """The batch name tag."""
82
88
  return self._tag
83
-
84
-
89
+
90
+
85
91
  @dataclass(frozen=True)
86
92
  class _BatchExtension:
87
93
  """Supported extensions for a `BaseBatch`, inherited by all derived classes.
88
-
94
+
89
95
  :ivar PNG: Corresponds to the `.png` file extension.
90
96
  """
97
+
91
98
  PNG: str = "png"
92
-
93
-
99
+
100
+
94
101
  class _PngFile(BatchFile[str]):
95
102
  """Stores an image visualising the data for the batch."""
96
- def __init__(
97
- self,
98
- batch_parent_dir_path: str,
99
- batch_name: str
100
- ) -> None:
103
+
104
+ def __init__(self, batch_parent_dir_path: str, batch_name: str) -> None:
101
105
  """Initialise a `_PngFile` instance.
102
106
 
103
107
  :param batch_parent_dir_path: The parent directory for the batch.
@@ -105,7 +109,6 @@ class _PngFile(BatchFile[str]):
105
109
  """
106
110
  super().__init__(batch_parent_dir_path, batch_name, _BatchExtension.PNG)
107
111
 
108
-
109
112
  def _read(self) -> str:
110
113
  """Reads the PNG file and returns it base64-encoded.
111
114
 
@@ -114,25 +117,22 @@ class _PngFile(BatchFile[str]):
114
117
  with open(self.file_path, "rb") as f:
115
118
  encoded = b64encode(f.read())
116
119
  return encoded.decode("ascii")
117
-
118
-
120
+
121
+
119
122
  class BaseBatch(ABC):
120
123
  """
121
124
  An abstract base class representing a group of data files over a common time interval.
122
125
 
123
126
  All files in a batch share a base file name and differ only by their extension.
124
- Subclasses of `BaseBatch` define the expected data for each file extension and
127
+ Subclasses of `BaseBatch` define the expected data for each file extension and
125
128
  provide an API for accessing their contents using `BatchFile` subclasses.
126
129
 
127
130
  Subclasses should expose `BatchFile` instances directly as attributes, which
128
131
  simplifies static typing. Additionally, they should call `add_file` in the constructor
129
132
  to formally register each `BatchFile`.
130
133
  """
131
- def __init__(
132
- self,
133
- start_time: str,
134
- tag: str
135
- ) -> None:
134
+
135
+ def __init__(self, start_time: str, tag: str) -> None:
136
136
  """Initialise a `BaseBatch` instance.
137
137
 
138
138
  :param start_time: Start time of the batch as a string with seconds precision.
@@ -141,113 +141,82 @@ class BaseBatch(ABC):
141
141
  self._start_time = start_time
142
142
  self._tag: str = tag
143
143
  self._start_datetime = datetime.strptime(self.start_time, TimeFormat.DATETIME)
144
- self._parent_dir_path = get_batches_dir_path(year = self.start_datetime.year,
145
- month = self.start_datetime.month,
146
- day = self.start_datetime.day)
147
-
144
+ self._parent_dir_path = get_batches_dir_path(
145
+ year=self.start_datetime.year,
146
+ month=self.start_datetime.month,
147
+ day=self.start_datetime.day,
148
+ )
149
+
148
150
  # internal register of batch files
149
151
  self._batch_files: dict[str, BatchFile] = {}
150
-
152
+
151
153
  # Add the files shared by all derived classes
152
- self._png_file = _PngFile(self.parent_dir_path, self.name)
153
- self.add_file( self._png_file )
154
-
155
-
154
+ self._png_file = _PngFile(self.parent_dir_path, self.name)
155
+ self.add_file(self._png_file)
156
+
156
157
  @property
157
158
  @abstractmethod
158
- def spectrogram_file(
159
- self
160
- ) -> BatchFile:
159
+ def spectrogram_file(self) -> BatchFile:
161
160
  """The batch file which contains spectrogram data."""
162
-
163
-
161
+
164
162
  @property
165
- def start_time(
166
- self
167
- ) -> str:
163
+ def start_time(self) -> str:
168
164
  """The start time of the batch, formatted as a string up to seconds precision."""
169
165
  return self._start_time
170
166
 
171
-
172
167
  @property
173
- def start_datetime(
174
- self
175
- ) -> datetime:
168
+ def start_datetime(self) -> datetime:
176
169
  """The start time of the batch, parsed as a datetime up to seconds precision."""
177
170
  return self._start_datetime
178
-
179
-
171
+
180
172
  @property
181
- def tag(
182
- self
183
- ) -> str:
173
+ def tag(self) -> str:
184
174
  """The batch name tag."""
185
175
  return self._tag
186
-
187
176
 
188
177
  @property
189
- def parent_dir_path(
190
- self
191
- ) -> str:
178
+ def parent_dir_path(self) -> str:
192
179
  """The parent directory for the batch."""
193
180
  return self._parent_dir_path
194
181
 
195
-
196
182
  @property
197
- def name(
198
- self
199
- ) -> str:
200
- """Return the base file name shared by all files in the batch,
183
+ def name(self) -> str:
184
+ """Return the base file name shared by all files in the batch,
201
185
  composed of the start time and the batch tag."""
202
186
  return f"{self._start_time}_{self._tag}"
203
-
204
-
187
+
205
188
  @property
206
- def extensions(
207
- self
208
- ) -> list[str]:
189
+ def extensions(self) -> list[str]:
209
190
  """All defined file extensions for the batch."""
210
191
  return list(self._batch_files.keys())
211
-
212
-
192
+
213
193
  @property
214
- def batch_files(
215
- self
216
- ) -> dict[str, BatchFile]:
194
+ def batch_files(self) -> dict[str, BatchFile]:
217
195
  """Map each file extension in the batch to the corresponding batch file instance.
218
-
196
+
219
197
  Use `add_file` to add a file to the batch.
220
198
  """
221
199
  return self._batch_files
222
-
223
-
200
+
224
201
  @property
225
- def png_file(
226
- self
227
- ) -> _PngFile:
202
+ def png_file(self) -> _PngFile:
228
203
  """The batch file corresponding to the `.png` extension."""
229
204
  return self._png_file
230
-
231
205
 
232
- def add_file(
233
- self,
234
- batch_file: BatchFile
235
- ) -> None:
206
+ def add_file(self, batch_file: BatchFile) -> None:
236
207
  """Add an instance of a batch file to the batch.
237
208
 
238
209
  :param batch_file: The `BatchFile` instance to add to the batch.
239
210
  :raises ValueError: If the `BatchFile` instance does not have a defined file extension.
240
211
  """
241
212
  if batch_file.extension is None:
242
- raise ValueError(f"The `BatchFile` must have a defined file extension. "
243
- f"Received '{batch_file.extension}.")
213
+ raise ValueError(
214
+ f"The `BatchFile` must have a defined file extension. "
215
+ f"Received '{batch_file.extension}."
216
+ )
244
217
  self._batch_files[batch_file.extension] = batch_file
245
-
246
218
 
247
- def get_file(
248
- self,
249
- extension: str
250
- ) -> BatchFile:
219
+ def get_file(self, extension: str) -> BatchFile:
251
220
  """Get a batch file instance from the batch, according to the file extension.
252
221
 
253
222
  :param extension: The file extension of the batch file.
@@ -257,13 +226,11 @@ class BaseBatch(ABC):
257
226
  try:
258
227
  return self._batch_files[extension]
259
228
  except KeyError:
260
- raise NotImplementedError(f"A batch file with extension '{extension}' is not implemented for this batch.")
261
-
229
+ raise NotImplementedError(
230
+ f"A batch file with extension '{extension}' is not implemented for this batch."
231
+ )
262
232
 
263
- def delete_file(
264
- self,
265
- extension: str
266
- ) -> None:
233
+ def delete_file(self, extension: str) -> None:
267
234
  """Delete a file from the batch, according to the file extension.
268
235
 
269
236
  :param extension: The file extension of the batch file.
@@ -272,11 +239,7 @@ class BaseBatch(ABC):
272
239
  batch_file = self.get_file(extension)
273
240
  batch_file.delete()
274
241
 
275
-
276
- def has_file(
277
- self,
278
- extension: str
279
- ) -> bool:
242
+ def has_file(self, extension: str) -> bool:
280
243
  """Determine the existance of a batch file in the file system.
281
244
 
282
245
  :param extension: The file extension of the batch file.
@@ -287,22 +250,10 @@ class BaseBatch(ABC):
287
250
  return batch_file.exists
288
251
  except FileNotFoundError:
289
252
  return False
290
-
291
-
292
- def read_spectrogram(
293
- self
294
- ) -> Spectrogram:
253
+
254
+ def read_spectrogram(self) -> Spectrogram:
295
255
  """Read and return the spectrogram data stored in the batch.
296
256
 
297
257
  :return: The spectrogram stored by the batch `spectrogram_file`.
298
258
  """
299
259
  return self.spectrogram_file.read()
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-