sl-shared-assets 3.0.0rc14__py3-none-any.whl → 3.0.1__py3-none-any.whl

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

Potentially problematic release.


This version of sl-shared-assets might be problematic. Click here for more details.

sl_shared_assets/cli.py CHANGED
@@ -49,11 +49,11 @@ def verify_session_integrity(
49
49
  """Checks the integrity of the target session's raw data (contents of the raw_data directory).
50
50
 
51
51
  This command assumes that the data has been checksummed during acquisition and contains an ax_checksum.txt file
52
- that stores the data checksum generated before transferring the data to long-term storage destination. This function
53
- always verified the integrity of the 'raw_data' directory. It does not work with 'processed_data' or any other
54
- directories. If the session data was corrupted, the command removes the 'telomere.bin' file, marking the session as
55
- 'incomplete' and automatically excluding it from all further automated processing runtimes. if the session data
56
- is intact, generates a 'verified.bin' marker file inside the session's raw_data folder.
52
+ that stores the data checksum generated before transferring the data to the long-term storage destination. This
53
+ function always verified the integrity of the 'raw_data' directory. It does not work with 'processed_data' or any
54
+ other directories. If the session data was corrupted, the command removes the 'telomere.bin' file, marking the
55
+ session as 'incomplete' and automatically excluding it from all further automated processing runtimes. If the
56
+ session data is intact, it generates a 'verified.bin' marker file inside the session's raw_data folder.
57
57
 
58
58
  The command is also used by Sun lab data acquisition systems to generate the processed data hierarchy for each
59
59
  processed session. This use case is fully automated and should not be triggered manually by the user.
@@ -366,7 +366,7 @@ def start_jupyter_server(
366
366
  server = Server(credentials_path)
367
367
  job: JupyterJob | None = None
368
368
  try:
369
- # If the caller did not provide an explicit notebook directory, defaults to user's working directory
369
+ # If the caller did not provide an explicit notebook directory, defaults to the user's working directory
370
370
  if directory is None:
371
371
  directory = (server.user_working_root,)
372
372
 
@@ -394,7 +394,7 @@ def start_jupyter_server(
394
394
  if job is not None:
395
395
  server.abort_job(job)
396
396
 
397
- # Closes server connection if it is still open
397
+ # Closes the server connection if it is still open
398
398
  server.close()
399
399
 
400
400
 
@@ -446,10 +446,6 @@ def resolve_dataset_marker(
446
446
  processing pipelines are not allowed to work with the session data, ensuring that all processed data remains
447
447
  unchanged. If the marker does not exist, dataset integration pipelines are not allowed to work with the session
448
448
  data, enabling processing pipelines to safely modify the data at any time.
449
-
450
- This command is automatically called at the end of each processing runtime to automatically transfer processed
451
- sessions to the dataset integration step by creating the p53.bin marker. In contrast, removing the marker can only
452
- be done manually.
453
449
  """
454
450
  resolve_p53_marker(
455
451
  session_path=session_path,
sl_shared_assets/cli.pyi CHANGED
@@ -22,11 +22,11 @@ def verify_session_integrity(
22
22
  """Checks the integrity of the target session's raw data (contents of the raw_data directory).
23
23
 
24
24
  This command assumes that the data has been checksummed during acquisition and contains an ax_checksum.txt file
25
- that stores the data checksum generated before transferring the data to long-term storage destination. This function
26
- always verified the integrity of the 'raw_data' directory. It does not work with 'processed_data' or any other
27
- directories. If the session data was corrupted, the command removes the 'telomere.bin' file, marking the session as
28
- 'incomplete' and automatically excluding it from all further automated processing runtimes. if the session data
29
- is intact, generates a 'verified.bin' marker file inside the session's raw_data folder.
25
+ that stores the data checksum generated before transferring the data to the long-term storage destination. This
26
+ function always verified the integrity of the 'raw_data' directory. It does not work with 'processed_data' or any
27
+ other directories. If the session data was corrupted, the command removes the 'telomere.bin' file, marking the
28
+ session as 'incomplete' and automatically excluding it from all further automated processing runtimes. If the
29
+ session data is intact, it generates a 'verified.bin' marker file inside the session's raw_data folder.
30
30
 
31
31
  The command is also used by Sun lab data acquisition systems to generate the processed data hierarchy for each
32
32
  processed session. This use case is fully automated and should not be triggered manually by the user.
@@ -94,8 +94,4 @@ def resolve_dataset_marker(
94
94
  processing pipelines are not allowed to work with the session data, ensuring that all processed data remains
95
95
  unchanged. If the marker does not exist, dataset integration pipelines are not allowed to work with the session
96
96
  data, enabling processing pipelines to safely modify the data at any time.
97
-
98
- This command is automatically called at the end of each processing runtime to automatically transfer processed
99
- sessions to the dataset integration step by creating the p53.bin marker. In contrast, removing the marker can only
100
- be done manually.
101
97
  """
@@ -1,7 +1,7 @@
1
1
  """This package provides the classes used to store data acquired at various stages of the data workflow and to
2
2
  configure various pipelines used in the Sun lab. These classes are used across all stages of data acquisition,
3
- preprocessing, and processing in the lab that run on multiple machines (PCs). Many classes in this package are designed
4
- to be saved to disk as .yaml files and restored from the .yaml files as needed."""
3
+ preprocessing, and processing in the lab. Many classes in this package are designed to be saved to disk as .yaml files
4
+ and restored from the .yaml files as needed."""
5
5
 
6
6
  from .runtime_data import (
7
7
  ZaberPositions,
@@ -24,10 +24,10 @@ class AcquisitionSystems(StrEnum):
24
24
  class ExperimentState:
25
25
  """Encapsulates the information used to set and maintain the desired experiment and system state.
26
26
 
27
- Broadly, each experiment runtime can be conceptualized as a two state-system. The first is the experiment task,
27
+ Broadly, each experiment runtime can be conceptualized as a two-state-system. The first is the experiment task,
28
28
  which reflects the behavior goal, the rules for achieving the goal, and the reward for achieving the goal. The
29
29
  second is the data acquisition system state, which is a snapshot of all hardware module states that make up the
30
- system that acquires the data and controls the task environment. Overall, experiment state is about
30
+ system that acquires the data and controls the task environment. Overall, the experiment state is about
31
31
  'what the animal is doing', while the system state is about 'what the hardware is doing'.
32
32
 
33
33
  Note:
@@ -96,16 +96,16 @@ class ExperimentTrial:
96
96
  class MesoscopeExperimentConfiguration(YamlConfig):
97
97
  """Stores the configuration of a single experiment runtime that uses the Mesoscope_VR data acquisition system.
98
98
 
99
- Primarily, this includes the sequence of experiment and system states that defines the flow of the experiment
99
+ Primarily, this includes the sequence of experiment and system states that define the flow of the experiment
100
100
  runtime and the configuration of various trials supported by the experiment runtime. During runtime, the main
101
101
  runtime control function traverses the sequence of states stored in this class instance start-to-end in the exact
102
- order specified by the user. Together with custom Unity projects that define the task logic (how the system
103
- responds to animal interactions with the VR system) this class allows flexibly implementing a wide range of
102
+ order specified by the user. Together with custom Unity projects, which define the task logic (how the system
103
+ responds to animal interactions with the VR system), this class allows flexibly implementing a wide range of
104
104
  experiments using the Mesoscope-VR system.
105
105
 
106
106
  Each project should define one or more experiment configurations and save them as .yaml files inside the project
107
107
  'configuration' folder. The name for each configuration file is defined by the user and is used to identify and load
108
- the experiment configuration when 'sl-experiment' CLI command exposed by the sl-experiment library is executed.
108
+ the experiment configuration when the 'sl-experiment' CLI command exposed by the sl-experiment library is executed.
109
109
 
110
110
  Notes:
111
111
  This class is designed exclusively for the Mesoscope-VR system. Any other system needs to define a separate
@@ -126,6 +126,9 @@ class MesoscopeExperimentConfiguration(YamlConfig):
126
126
  track. This offset statically shifts the entire track (in centimeters) against the set of VR wall cues used during
127
127
  runtime. Storing this static offset as part of experiment configuration is crucial for correctly mapping what the
128
128
  animal sees during runtime to the real-world distance it travels on the running wheel."""
129
+ unity_scene_name: str = "IvanScene"
130
+ """The name of the Virtual Reality task (Unity Scene) used during experiment. This is used as an extra security
131
+ measure to ensure that Unity game engine is running the correct scene when starting the experiment runtime."""
129
132
  experiment_states: dict[str, ExperimentState] = field(
130
133
  default_factory=lambda: {
131
134
  "baseline": ExperimentState(
@@ -415,7 +418,7 @@ class MesoscopeSystemConfiguration(YamlConfig):
415
418
  self.paths.mesoscope_directory = Path(self.paths.mesoscope_directory)
416
419
  self.paths.harvesters_cti_path = Path(self.paths.harvesters_cti_path)
417
420
 
418
- # Converts valve_calibration data from dictionary to a tuple of tuples format
421
+ # Converts valve_calibration data from a dictionary to a tuple of tuples format
419
422
  if not isinstance(self.microcontrollers.valve_calibration_data, tuple):
420
423
  self.microcontrollers.valve_calibration_data = tuple(
421
424
  (k, v) for k, v in self.microcontrollers.valve_calibration_data.items()
@@ -483,7 +486,7 @@ def set_system_configuration_file(path: Path) -> None:
483
486
  the managed machine (PC).
484
487
 
485
488
  This function is used to initially configure or override the existing configuration of any data acquisition system
486
- used in the lab. The path to the configuration file is stored inside the user's data directory, so that all
489
+ used in the lab. The path to the configuration file is stored inside the user's data directory so that all
487
490
  Sun lab libraries can automatically access that information during every runtime. Since the storage directory is
488
491
  typically hidden and varies between OSes and machines, this function provides a convenient way for setting that
489
492
  path without manually editing the storage cache.
@@ -14,10 +14,10 @@ class AcquisitionSystems(StrEnum):
14
14
  class ExperimentState:
15
15
  """Encapsulates the information used to set and maintain the desired experiment and system state.
16
16
 
17
- Broadly, each experiment runtime can be conceptualized as a two state-system. The first is the experiment task,
17
+ Broadly, each experiment runtime can be conceptualized as a two-state-system. The first is the experiment task,
18
18
  which reflects the behavior goal, the rules for achieving the goal, and the reward for achieving the goal. The
19
19
  second is the data acquisition system state, which is a snapshot of all hardware module states that make up the
20
- system that acquires the data and controls the task environment. Overall, experiment state is about
20
+ system that acquires the data and controls the task environment. Overall, the experiment state is about
21
21
  'what the animal is doing', while the system state is about 'what the hardware is doing'.
22
22
 
23
23
  Note:
@@ -53,16 +53,16 @@ class ExperimentTrial:
53
53
  class MesoscopeExperimentConfiguration(YamlConfig):
54
54
  """Stores the configuration of a single experiment runtime that uses the Mesoscope_VR data acquisition system.
55
55
 
56
- Primarily, this includes the sequence of experiment and system states that defines the flow of the experiment
56
+ Primarily, this includes the sequence of experiment and system states that define the flow of the experiment
57
57
  runtime and the configuration of various trials supported by the experiment runtime. During runtime, the main
58
58
  runtime control function traverses the sequence of states stored in this class instance start-to-end in the exact
59
- order specified by the user. Together with custom Unity projects that define the task logic (how the system
60
- responds to animal interactions with the VR system) this class allows flexibly implementing a wide range of
59
+ order specified by the user. Together with custom Unity projects, which define the task logic (how the system
60
+ responds to animal interactions with the VR system), this class allows flexibly implementing a wide range of
61
61
  experiments using the Mesoscope-VR system.
62
62
 
63
63
  Each project should define one or more experiment configurations and save them as .yaml files inside the project
64
64
  'configuration' folder. The name for each configuration file is defined by the user and is used to identify and load
65
- the experiment configuration when 'sl-experiment' CLI command exposed by the sl-experiment library is executed.
65
+ the experiment configuration when the 'sl-experiment' CLI command exposed by the sl-experiment library is executed.
66
66
 
67
67
  Notes:
68
68
  This class is designed exclusively for the Mesoscope-VR system. Any other system needs to define a separate
@@ -73,6 +73,7 @@ class MesoscopeExperimentConfiguration(YamlConfig):
73
73
 
74
74
  cue_map: dict[int, float] = field(default_factory=Incomplete)
75
75
  cue_offset_cm: float = ...
76
+ unity_scene_name: str = ...
76
77
  experiment_states: dict[str, ExperimentState] = field(default_factory=Incomplete)
77
78
  trial_structures: dict[str, ExperimentTrial] = field(default_factory=Incomplete)
78
79
 
@@ -197,7 +198,7 @@ def set_system_configuration_file(path: Path) -> None:
197
198
  the managed machine (PC).
198
199
 
199
200
  This function is used to initially configure or override the existing configuration of any data acquisition system
200
- used in the lab. The path to the configuration file is stored inside the user's data directory, so that all
201
+ used in the lab. The path to the configuration file is stored inside the user's data directory so that all
201
202
  Sun lab libraries can automatically access that information during every runtime. Since the storage directory is
202
203
  typically hidden and varies between OSes and machines, this function provides a convenient way for setting that
203
204
  path without manually editing the storage cache.
@@ -35,7 +35,7 @@ class MesoscopeHardwareState(YamlConfig):
35
35
  any field in this dataclass to None also functions as a flag for whether to parse the log associated with the
36
36
  module that provides this field's information.
37
37
 
38
- This class is automatically configured by _MesoscopeVRSystem class from sl-experiment library to facilitate
38
+ This class is automatically configured by _MesoscopeVRSystem class from the sl-experiment library to facilitate
39
39
  proper log parsing.
40
40
  """
41
41
 
@@ -200,7 +200,7 @@ class WindowCheckingDescriptor(YamlConfig):
200
200
 
201
201
  Notes:
202
202
  Window Checking sessions are different from all other sessions. Unlike other sessions, their purpose is not to
203
- generate data, but rather to assess the suitability of the particular animal to be included in training and
203
+ generate data but rather to assess the suitability of the particular animal to be included in training and
204
204
  experiment cohorts. These sessions are automatically excluded from any automated data processing and analysis.
205
205
  """
206
206
 
@@ -28,7 +28,7 @@ class MesoscopeHardwareState(YamlConfig):
28
28
  any field in this dataclass to None also functions as a flag for whether to parse the log associated with the
29
29
  module that provides this field's information.
30
30
 
31
- This class is automatically configured by _MesoscopeVRSystem class from sl-experiment library to facilitate
31
+ This class is automatically configured by _MesoscopeVRSystem class from the sl-experiment library to facilitate
32
32
  proper log parsing.
33
33
  """
34
34
 
@@ -102,7 +102,7 @@ class WindowCheckingDescriptor(YamlConfig):
102
102
 
103
103
  Notes:
104
104
  Window Checking sessions are different from all other sessions. Unlike other sessions, their purpose is not to
105
- generate data, but rather to assess the suitability of the particular animal to be included in training and
105
+ generate data but rather to assess the suitability of the particular animal to be included in training and
106
106
  experiment cohorts. These sessions are automatically excluded from any automated data processing and analysis.
107
107
  """
108
108
 
@@ -29,7 +29,7 @@ class SessionTypes(StrEnum):
29
29
 
30
30
  Notes:
31
31
  This enumeration does not differentiate between different acquisition systems. Different acquisition systems
32
- support different session types, and may not be suited for acquiring some of the session types listed in this
32
+ support different session types and may not be suited for acquiring some of the session types listed in this
33
33
  enumeration.
34
34
  """
35
35
 
@@ -288,7 +288,7 @@ class SessionData(YamlConfig):
288
288
  name. If the session is not an experiment session, this field should be left as Null (None)."""
289
289
  python_version: str = "3.11.13"
290
290
  """Stores the Python version that was used to acquire session data."""
291
- sl_experiment_version: str = "2.0.0"
291
+ sl_experiment_version: str = "3.0.0"
292
292
  """Stores the version of the sl-experiment library that was used to acquire the session data."""
293
293
  raw_data: RawData = field(default_factory=lambda: RawData())
294
294
  """Stores absolute paths to all directories and files that jointly make the session's raw data hierarchy. This
@@ -405,7 +405,7 @@ class SessionData(YamlConfig):
405
405
  raw_data.resolve_paths(root_directory_path=session_path.joinpath("raw_data"))
406
406
  raw_data.make_directories() # Generates the local 'raw_data' directory tree
407
407
 
408
- # Resolves, but does not make processed_data directories. All runtimes that require access to 'processed_data'
408
+ # Resolves but does not make processed_data directories. All runtimes that require access to 'processed_data'
409
409
  # are configured to generate those directories if necessary, so there is no need to make them here.
410
410
  processed_data = ProcessedData()
411
411
  processed_data.resolve_paths(root_directory_path=session_path.joinpath("processed_data"))
@@ -425,18 +425,18 @@ class SessionData(YamlConfig):
425
425
  sl_experiment_version=sl_experiment_version,
426
426
  )
427
427
 
428
- # Saves the configured instance data to the session's folder, so that it can be reused during processing or
428
+ # Saves the configured instance data to the session's folder so that it can be reused during processing or
429
429
  # preprocessing.
430
430
  instance._save()
431
431
 
432
432
  # Also saves the SystemConfiguration and ExperimentConfiguration instances to the same folder using the paths
433
433
  # resolved for the RawData instance above.
434
434
 
435
- # Dumps the acquisition system's configuration data to session's folder
435
+ # Dumps the acquisition system's configuration data to the session's folder
436
436
  acquisition_system.save(path=instance.raw_data.system_configuration_path)
437
437
 
438
438
  if experiment_name is not None:
439
- # Copies the experiment_configuration.yaml file to session's folder
439
+ # Copies the experiment_configuration.yaml file to the session's folder
440
440
  experiment_configuration_path = acquisition_system.paths.root_directory.joinpath(
441
441
  project_name, "configuration", f"{experiment_name}.yaml"
442
442
  )
@@ -473,8 +473,8 @@ class SessionData(YamlConfig):
473
473
  provide the path to the root project directory (directory that stores all Sun lab projects) on that
474
474
  drive. The method will automatically resolve the project/animal/session/processed_data hierarchy using
475
475
  this root path. If raw and processed data are kept on the same drive, keep this set to None.
476
- make_processed_data_directory: Determines whether this method should create processed_data directory if it
477
- does not exist.
476
+ make_processed_data_directory: Determines whether this method should create the processed_data directory if
477
+ it does not exist.
478
478
 
479
479
  Returns:
480
480
  An initialized SessionData instance for the session whose data is stored at the provided path.
@@ -484,7 +484,7 @@ class SessionData(YamlConfig):
484
484
 
485
485
  """
486
486
  # To properly initialize the SessionData instance, the provided path should contain the raw_data directory
487
- # with session_data.yaml file.
487
+ # with the session_data.yaml file.
488
488
  session_data_path = session_path.joinpath("raw_data", "session_data.yaml")
489
489
  if not session_data_path.exists():
490
490
  message = (
@@ -495,7 +495,7 @@ class SessionData(YamlConfig):
495
495
  )
496
496
  console.error(message=message, error=FileNotFoundError)
497
497
 
498
- # Loads class data from .yaml file
498
+ # Loads class data from the .yaml file
499
499
  instance: SessionData = cls.from_yaml(file_path=session_data_path) # type: ignore
500
500
 
501
501
  # The method assumes that the 'donor' .yaml file is always stored inside the raw_data directory of the session
@@ -538,7 +538,7 @@ class SessionData(YamlConfig):
538
538
  def _save(self) -> None:
539
539
  """Saves the instance data to the 'raw_data' directory of the managed session as a 'session_data.yaml' file.
540
540
 
541
- This is used to save the data stored in the instance to disk, so that it can be reused during further stages of
541
+ This is used to save the data stored in the instance to disk so that it can be reused during further stages of
542
542
  data processing. The method is intended to only be used by the SessionData instance itself during its
543
543
  create() method runtime.
544
544
  """
@@ -547,9 +547,9 @@ class SessionData(YamlConfig):
547
547
  # processing
548
548
  origin = copy.deepcopy(self)
549
549
 
550
- # Resets all path fields to null. These fields are not loaded from disk when the instance is loaded, so setting
551
- # them to null has no negative consequences. Conversely, keeping these fields with Path objects prevents the
552
- # SessionData instance from being loaded from disk.
550
+ # Resets all path fields to null. These fields are not loaded from the disk when the instance is loaded, so
551
+ # setting them to null has no negative consequences. Conversely, keeping these fields with Path objects
552
+ # prevents the SessionData instance from being loaded from the disk.
553
553
  origin.raw_data = None # type: ignore
554
554
  origin.processed_data = None # type: ignore
555
555
 
@@ -585,6 +585,10 @@ class ProcessingTracker(YamlConfig):
585
585
  _lock_path: str = field(init=False)
586
586
  """Stores the path to the .lock file for the target tracker .yaml file. This file is used to ensure that only one
587
587
  process can simultaneously read from or write to the wrapped .yaml file."""
588
+ _started_runtime: bool = False
589
+ """This internal service field tracks when the class instance is used to start a runtime. It is set automatically by
590
+ the ProcessingTracker instance and is used to prevent runtime errors from deadlocking the specific processing
591
+ pipeline tracked by the class instance."""
588
592
 
589
593
  def __post_init__(self) -> None:
590
594
  # Generates the lock file for the target .yaml file path.
@@ -594,19 +598,18 @@ class ProcessingTracker(YamlConfig):
594
598
  self._lock_path = ""
595
599
 
596
600
  def __del__(self) -> None:
597
- """If the instance is garbage-collected without calling the stop() method, assumes this is due to a runtime
598
- error.
601
+ """If the instance as used to start a runtime, ensures that the instance properly marks the runtime as completed
602
+ or erred before being garbage-collected.
599
603
 
600
- It is essential to always resolve the runtime as either 'stopped' or 'erred' to avoid deadlocking the session
601
- data.
604
+ This is a security mechanism to prevent deadlocking the processed session and pipeline for future runtimes.
602
605
  """
603
- if self._is_running:
606
+ if self._started_runtime and self._is_running:
604
607
  self.error()
605
608
 
606
609
  def _load_state(self) -> None:
607
610
  """Reads the current processing state from the wrapped .YAML file."""
608
611
  if self.file_path.exists():
609
- # Loads the data for the state values, but does not replace the file path or lock attributes.
612
+ # Loads the data for the state values but does not replace the file path or lock attributes.
610
613
  instance: ProcessingTracker = self.from_yaml(self.file_path) # type: ignore
611
614
  self._is_complete = instance._is_complete
612
615
  self._encountered_error = instance._encountered_error
@@ -622,6 +625,7 @@ class ProcessingTracker(YamlConfig):
622
625
  original = copy.deepcopy(self)
623
626
  original.file_path = None # type: ignore
624
627
  original._lock_path = None # type: ignore
628
+ original._started_runtime = False # This field is only used by the instance stored in memory.
625
629
  original.to_yaml(file_path=self.file_path)
626
630
 
627
631
  def start(self) -> None:
@@ -631,13 +635,13 @@ class ProcessingTracker(YamlConfig):
631
635
  with an error.
632
636
 
633
637
  Raises:
634
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
638
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
635
639
  """
636
640
  try:
637
641
  # Acquires the lock
638
642
  lock = FileLock(self._lock_path)
639
643
  with lock.acquire(timeout=10.0):
640
- # Loads tracker state from .yaml file
644
+ # Loads tracker state from the .yaml file
641
645
  self._load_state()
642
646
 
643
647
  # If the runtime is already running, aborts with an error
@@ -656,6 +660,10 @@ class ProcessingTracker(YamlConfig):
656
660
  self._encountered_error = False
657
661
  self._save_state()
658
662
 
663
+ # Sets the start tracker flag to True, which ensures that the class tries to mark the runtime as
664
+ # completed or erred before it being garbage-collected.
665
+ self._started_runtime = True
666
+
659
667
  # If lock acquisition fails for any reason, aborts with an error
660
668
  except Timeout:
661
669
  message = (
@@ -676,14 +684,14 @@ class ProcessingTracker(YamlConfig):
676
684
  from the process that calls this method.
677
685
 
678
686
  Raises:
679
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
687
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
680
688
  """
681
689
 
682
690
  try:
683
691
  # Acquires the lock
684
692
  lock = FileLock(self._lock_path)
685
693
  with lock.acquire(timeout=10.0):
686
- # Loads tracker state from .yaml file
694
+ # Loads tracker state from the .yaml file
687
695
  self._load_state()
688
696
 
689
697
  # If the runtime is not running, aborts with an error
@@ -702,6 +710,9 @@ class ProcessingTracker(YamlConfig):
702
710
  self._encountered_error = True
703
711
  self._save_state()
704
712
 
713
+ # Disables the security flag
714
+ self._started_runtime = False
715
+
705
716
  # If lock acquisition fails for any reason, aborts with an error
706
717
  except Timeout:
707
718
  message = (
@@ -720,14 +731,14 @@ class ProcessingTracker(YamlConfig):
720
731
  at the end of the runtime.
721
732
 
722
733
  Raises:
723
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
734
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
724
735
  """
725
736
 
726
737
  try:
727
738
  # Acquires the lock
728
739
  lock = FileLock(self._lock_path)
729
740
  with lock.acquire(timeout=10.0):
730
- # Loads tracker state from .yaml file
741
+ # Loads tracker state from the .yaml file
731
742
  self._load_state()
732
743
 
733
744
  # If the runtime is not running, aborts with an error
@@ -746,6 +757,9 @@ class ProcessingTracker(YamlConfig):
746
757
  self._encountered_error = False
747
758
  self._save_state()
748
759
 
760
+ # Disables the security flag
761
+ self._started_runtime = False
762
+
749
763
  # If lock acquisition fails for any reason, aborts with an error
750
764
  except Timeout:
751
765
  message = (
@@ -764,7 +778,7 @@ class ProcessingTracker(YamlConfig):
764
778
  # Acquires the lock
765
779
  lock = FileLock(self._lock_path)
766
780
  with lock.acquire(timeout=10.0):
767
- # Loads tracker state from .yaml file
781
+ # Loads tracker state from the .yaml file
768
782
  self._load_state()
769
783
  return self._is_complete
770
784
 
@@ -786,7 +800,7 @@ class ProcessingTracker(YamlConfig):
786
800
  # Acquires the lock
787
801
  lock = FileLock(self._lock_path)
788
802
  with lock.acquire(timeout=10.0):
789
- # Loads tracker state from .yaml file
803
+ # Loads tracker state from the .yaml file
790
804
  self._load_state()
791
805
  return self._encountered_error
792
806
 
@@ -808,7 +822,7 @@ class ProcessingTracker(YamlConfig):
808
822
  # Acquires the lock
809
823
  lock = FileLock(self._lock_path)
810
824
  with lock.acquire(timeout=10.0):
811
- # Loads tracker state from .yaml file
825
+ # Loads tracker state from the .yaml file
812
826
  self._load_state()
813
827
  return self._is_running
814
828
 
@@ -20,7 +20,7 @@ class SessionTypes(StrEnum):
20
20
 
21
21
  Notes:
22
22
  This enumeration does not differentiate between different acquisition systems. Different acquisition systems
23
- support different session types, and may not be suited for acquiring some of the session types listed in this
23
+ support different session types and may not be suited for acquiring some of the session types listed in this
24
24
  enumeration.
25
25
  """
26
26
 
@@ -206,8 +206,8 @@ class SessionData(YamlConfig):
206
206
  provide the path to the root project directory (directory that stores all Sun lab projects) on that
207
207
  drive. The method will automatically resolve the project/animal/session/processed_data hierarchy using
208
208
  this root path. If raw and processed data are kept on the same drive, keep this set to None.
209
- make_processed_data_directory: Determines whether this method should create processed_data directory if it
210
- does not exist.
209
+ make_processed_data_directory: Determines whether this method should create the processed_data directory if
210
+ it does not exist.
211
211
 
212
212
  Returns:
213
213
  An initialized SessionData instance for the session whose data is stored at the provided path.
@@ -226,7 +226,7 @@ class SessionData(YamlConfig):
226
226
  def _save(self) -> None:
227
227
  """Saves the instance data to the 'raw_data' directory of the managed session as a 'session_data.yaml' file.
228
228
 
229
- This is used to save the data stored in the instance to disk, so that it can be reused during further stages of
229
+ This is used to save the data stored in the instance to disk so that it can be reused during further stages of
230
230
  data processing. The method is intended to only be used by the SessionData instance itself during its
231
231
  create() method runtime.
232
232
  """
@@ -245,13 +245,13 @@ class ProcessingTracker(YamlConfig):
245
245
  _encountered_error: bool = ...
246
246
  _is_running: bool = ...
247
247
  _lock_path: str = field(init=False)
248
+ _started_runtime: bool = ...
248
249
  def __post_init__(self) -> None: ...
249
250
  def __del__(self) -> None:
250
- """If the instance is garbage-collected without calling the stop() method, assumes this is due to a runtime
251
- error.
251
+ """If the instance as used to start a runtime, ensures that the instance properly marks the runtime as completed
252
+ or erred before beign garbage-collected.
252
253
 
253
- It is essential to always resolve the runtime as either 'stopped' or 'erred' to avoid deadlocking the session
254
- data.
254
+ This is a security mechanism to prevent deadlocking the processed session and pipeline for future runtimes.
255
255
  """
256
256
  def _load_state(self) -> None:
257
257
  """Reads the current processing state from the wrapped .YAML file."""
@@ -264,7 +264,7 @@ class ProcessingTracker(YamlConfig):
264
264
  with an error.
265
265
 
266
266
  Raises:
267
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
267
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
268
268
  """
269
269
  def error(self) -> None:
270
270
  """Configures the tracker file to indicate that the tracked processing runtime encountered an error and failed
@@ -276,7 +276,7 @@ class ProcessingTracker(YamlConfig):
276
276
  from the process that calls this method.
277
277
 
278
278
  Raises:
279
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
279
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
280
280
  """
281
281
  def stop(self) -> None:
282
282
  """Configures the tracker file to indicate that the tracked processing runtime has been completed successfully.
@@ -286,7 +286,7 @@ class ProcessingTracker(YamlConfig):
286
286
  at the end of the runtime.
287
287
 
288
288
  Raises:
289
- TimeoutError: If the file lock for the target .YAML file cannot be acquired within the timeout period.
289
+ TimeoutError: If the .lock file for the target .YAML file cannot be acquired within the timeout period.
290
290
  """
291
291
  @property
292
292
  def is_complete(self) -> bool:
@@ -1,5 +1,5 @@
1
1
  """This package provides the classes and methods used by all Sun lab libraries to submit remote jobs to the BioHPC
2
- and other compute servers. This package is also used across all Sun lab members private code to interface with the
2
+ and other compute servers. This package is also used across all Sun lab members' private code to interface with the
3
3
  shared server."""
4
4
 
5
5
  from .job import Job, JupyterJob
@@ -1,6 +1,6 @@
1
1
  """This module provides the core Job class, used as the starting point for all SLURM-managed job executed on lab compute
2
2
  server(s). Specifically, the Job class acts as a wrapper around the SLURM configuration and specific logic of each
3
- job. During runtime, Server class interacts with input job objects to manage their transfer and execution on the
3
+ job. During runtime, the Server class interacts with input job objects to manage their transfer and execution on the
4
4
  remote servers.
5
5
 
6
6
  Since version 3.0.0, this module also provides the specialized JupyterJob class used to launch remote Jupyter
@@ -97,8 +97,8 @@ class Job:
97
97
  Attributes:
98
98
  remote_script_path: Stores the path to the script file relative to the root of the remote server that runs the
99
99
  command.
100
- job_id: Stores the unique job identifier assigned by the SLURM manager to this job, when it is accepted for
101
- execution. This field initialized to None and is overwritten by the Server class that submits the job.
100
+ job_id: Stores the unique job identifier assigned by the SLURM manager to this job when it is accepted for
101
+ execution. This field is initialized to None and is overwritten by the Server class that submits the job.
102
102
  job_name: Stores the descriptive name of the SLURM job.
103
103
  _command: Stores the managed SLURM command object.
104
104
  """
@@ -174,7 +174,7 @@ class Job:
174
174
  # initialization would not work as expected.
175
175
  fixed_script_content = script_content.replace("\\$", "$")
176
176
 
177
- # Returns the script content to caller as a string
177
+ # Returns the script content to the caller as a string
178
178
  return fixed_script_content
179
179
 
180
180
 
@@ -202,8 +202,8 @@ class JupyterJob(Job):
202
202
  conda_environment: The name of the conda environment to activate on the server before running the job logic. The
203
203
  environment should contain the necessary Python packages and CLIs to support running the job's logic. For
204
204
  Jupyter jobs, this necessarily includes the Jupyter notebook and jupyterlab packages.
205
- port: The connection port number for Jupyter server. Do not change the default value unless you know what you
206
- are doing, as the server has most common communication ports closed for security reasons.
205
+ port: The connection port number for the Jupyter server. Do not change the default value unless you know what
206
+ you are doing, as the server has most common communication ports closed for security reasons.
207
207
  notebook_directory: The directory to use as Jupyter's root. During runtime, Jupyter will only have access to
208
208
  items stored in or under this directory. For most runtimes, this should be set to the user's root data or
209
209
  working directory.
@@ -270,7 +270,7 @@ class JupyterJob(Job):
270
270
  self._build_jupyter_command(jupyter_args)
271
271
 
272
272
  def _build_jupyter_command(self, jupyter_args: str) -> None:
273
- """Builds the command to launch Jupyter notebook server on the remote Sun lab server."""
273
+ """Builds the command to launch the Jupyter notebook server on the remote Sun lab server."""
274
274
 
275
275
  # Gets the hostname of the compute node and caches it in the connection data file. Also caches the port name.
276
276
  self.add_command('echo "COMPUTE_NODE: $(hostname)" > {}'.format(self.connection_info_file))
@@ -297,7 +297,7 @@ class JupyterJob(Job):
297
297
  if jupyter_args:
298
298
  jupyter_cmd.append(jupyter_args)
299
299
 
300
- # Adds resolved jupyter command to the list of job commands.
300
+ # Adds the resolved jupyter command to the list of job commands.
301
301
  jupyter_cmd_str = " ".join(jupyter_cmd)
302
302
  self.add_command(jupyter_cmd_str)
303
303
 
@@ -324,7 +324,7 @@ class JupyterJob(Job):
324
324
  message = f"Could not parse connection information file for the Jupyter server job with id {self.job_id}."
325
325
  console.error(message, ValueError)
326
326
 
327
- # Stores extracted data inside connection_info attribute as a JupyterConnectionInfo instance.
327
+ # Stores extracted data inside the connection_info attribute as a JupyterConnectionInfo instance.
328
328
  self.connection_info = _JupyterConnectionInfo(
329
329
  compute_node=compute_node_match.group(1).strip(), # type: ignore
330
330
  port=int(port_match.group(1)), # type: ignore
@@ -352,7 +352,7 @@ class JupyterJob(Job):
352
352
  )
353
353
  return # No connection information available, so does not proceed with printing.
354
354
 
355
- # Prints generic connection details to terminal
355
+ # Prints generic connection details to the terminal
356
356
  console.echo(f"Jupyter is running on: {self.connection_info.compute_node}")
357
357
  console.echo(f"Port: {self.connection_info.port}")
358
358
  console.echo(f"Token: {self.connection_info.token}")