From 86d9cda0adef44b80947d29dc1db1a8d0f0bfb4d Mon Sep 17 00:00:00 2001 From: Phil Starkey Date: Thu, 5 Nov 2020 18:43:56 +1100 Subject: [PATCH 1/3] Renaming Run to Shot Resolves Rename Run to Shot #81 Renamed classes/arguments/docs from run to shot and added dprecation notices where appropriate. Arguments (distinct from keyword arguments) were renamed without worrying about backwards compatibility under the assumption that people are not using them as keyword arguments (which is technically possible). --- lyse/__init__.py | 67 +++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/lyse/__init__.py b/lyse/__init__.py index 2a30b8c..c2c249f 100644 --- a/lyse/__init__.py +++ b/lyse/__init__.py @@ -13,12 +13,14 @@ from lyse.dataframe_utilities import get_series_from_shot as _get_singleshot from labscript_utils.dict_diff import dict_diff +import copy import os import socket import pickle as pickle import inspect import sys import threading +import warnings import labscript_utils.h5_lock, h5py from labscript_utils.labconfig import LabConfig @@ -79,7 +81,7 @@ class _RoutineStorage(object): def data(filepath=None, host='localhost', port=_lyse_port, timeout=5, n_sequences=None, filter_kwargs=None): """Get data from the lyse dataframe or a file. - This function allows for either extracting information from a run's hdf5 + This function allows for either extracting information from a shots's hdf5 file, or retrieving data from lyse's dataframe. If `filepath` is provided then data will be read from that file and returned as a pandas series. If `filepath` is not provided then the dataframe in lyse, or a portion of it, @@ -97,11 +99,11 @@ def data(filepath=None, host='localhost', port=_lyse_port, timeout=5, n_sequence that method. Args: - filepath (str, optional): The path to a run's hdf5 file. If a value + filepath (str, optional): The path to a shot's hdf5 file. If a value other than `None` is provided, then this function will return a - pandas series containing data associated with the run. In particular + pandas series containing data associated with the shot. In particular it will contain the globals, singleshot results, multishot results, - etc. that would appear in the run's row in the Lyse dataframe, but + etc. that would appear in the shot's row in the Lyse dataframe, but the values will be read from the file rather than extracted from the lyse dataframe. If `filepath` is `None, then this function will instead return a section of the lyse dataframe. Note that if @@ -203,10 +205,10 @@ def _rangeindex_to_multiindex(df, inplace): df.sort_index(inplace=True) return df -def globals_diff(run1, run2, group=None): - return dict_diff(run1.get_globals(group), run2.get_globals(group)) +def globals_diff(shot1, shot2, group=None): + return dict_diff(shot1.get_globals(group), shot2.get_globals(group)) -class Run(object): +class Shot(object): """A class for saving/retrieving data to/from a shot's hdf5 file. This class implements methods that allow the user to retrieve data from a @@ -229,9 +231,9 @@ def __init__(self,h5_path,no_write=False): try: if not self.no_write: - # The group where this run's results will be stored in the h5 + # The group where this shot's results will be stored in the h5 # file will be the name of the python script which is - # instantiating this Run object. Iterate from innermost caller + # instantiating this Shot object. Iterate from innermost caller # to outermost. The name of the script will be one frame in # from analysis_subprocess.py. analysis_subprocess_path = os.path.join( @@ -256,7 +258,7 @@ def __init__(self,h5_path,no_write=False): self.set_group(group) except KeyError: # sys.stderr.write('Warning: to write results, call ' - # 'Run.set_group(groupname), specifying the name of the group ' + # 'Shot.set_group(groupname), specifying the name of the group ' # 'you would like to save results to. This normally comes from ' # 'the filename of your script, but since you\'re in interactive ' # 'mode, there is no script name.\n') @@ -284,7 +286,7 @@ def no_write(self): def group(self): """str: The group in the hdf5 file in which results are saved by default. - When a `Run` instance is created from within a lyse singleshot or + When a `Shot` instance is created from within a lyse singleshot or multishot routine, `group` will be set to the name of the running routine. If created from outside a lyse script it will be set to `None`. To change the default group for saving results, use the `set_group()` @@ -313,7 +315,7 @@ def _create_group_if_not_exists(self, h5_path, location, groupname): create_group = True if create_group: if self.no_write: - msg = "Cannot create group; this run is read-only." + msg = "Cannot create group; this shot is read-only." raise PermissionError(msg) with h5py.File(h5_path, 'r+') as h5_file: # Catch the ValueError raised if the group was created by @@ -566,10 +568,10 @@ def save_results(self, *args, **kwargs): for the optional arguments of `self.save_result()`. Examples: - >>> run = Run('path/to/an/hdf5/file.h5') # doctest: +SKIP + >>> shot = Shot('path/to/an/hdf5/file.h5') # doctest: +SKIP >>> a = 5 >>> b = 2.48 - >>> run.save_results('result', a, 'other_result', b, overwrite=False) # doctest: +SKIP + >>> shot.save_results('result', a, 'other_result', b, overwrite=False) # doctest: +SKIP """ names = args[::2] values = args[1::2] @@ -703,12 +705,21 @@ def globals_groups(self): except KeyError: return [] - def globals_diff(self, other_run, group=None): - return globals_diff(self, other_run, group) + def globals_diff(self, other_shot, group=None): + return globals_diff(self, other_shot, group) - -class Sequence(Run): - def __init__(self, h5_path, run_paths, no_write=False): + +class Run(Shot): + def __init__(self, *args, **kwargs): + warnings.warn("The 'Run' class has been renamed to 'Shot' (but is otherwise compatible). 'Run' will be removed in lyse v4.0. Please update your analysis code to use the 'Shot` class", DeprecationWarning) + super().__init__(*args, **kwargs) + + def globals_diff(self, other_run, group=None): + return globals_diff(self, other_run, group) + + +class Sequence(Shot): + def __init__(self, h5_path, shot_paths, no_write=False): # Ensure file exists without affecting its last modification time if it # already exists. try: @@ -724,15 +735,23 @@ def __init__(self, h5_path, run_paths, no_write=False): super().__init__(h5_path, no_write=no_write) - if isinstance(run_paths, pandas.DataFrame): - run_paths = run_paths['filepath'] - self.runs = {path: Run(path,no_write=True) for path in run_paths} + if isinstance(shot_paths, pandas.DataFrame): + shot_paths = shot_paths['filepath'] + self.__shots = {path: Shot(path,no_write=True) for path in shot_paths} + + @property + def runs(self): + warnings.warn("The 'runs' attribute has been renamed to 'shots'. 'runs' will be removed in lyse v4.0. Please update your analysis code to use the 'shots` attribute", DeprecationWarning) + return copy.copy(self.__shots) + + def shots(self): + return copy.copy(self.__shots) def get_trace(self,*args): - return {path:run.get_trace(*args) for path,run in self.runs.items()} + return {path:shot.get_trace(*args) for path,shot in self.shots.items()} def get_result_array(self,*args): - return {path:run.get_result_array(*args) for path,run in self.runs.items()} + return {path:shot.get_result_array(*args) for path,shot in self.shots.items()} def get_traces(self,*args): raise NotImplementedError('If you want to use this feature please ask me to implement it! -Chris') From 016768d95421476df847b3619f580872c27a680b Mon Sep 17 00:00:00 2001 From: Phil Starkey Date: Fri, 6 Nov 2020 16:01:59 +1100 Subject: [PATCH 2/3] Addressed suggested changes from @zakv in PR review * split deprecation messages across multiple lines * Fixed deprecation message punctuation * Fixed missing property decorator in `Sequence` class * Updated docs to use `Shot` class. --- docs/source/api/index.rst | 2 +- docs/source/api/lyse_functions.rst | 2 +- docs/source/api/{run.rst => shot.rst} | 6 +++--- docs/source/examples.rst | 8 ++++---- docs/source/introduction.rst | 2 +- lyse/__init__.py | 13 +++++++++++-- 6 files changed, 21 insertions(+), 12 deletions(-) rename docs/source/api/{run.rst => shot.rst} (55%) diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 33314d3..ea94b50 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -6,7 +6,7 @@ API Reference :maxdepth: 2 lyse_functions - run + shot sequence dataframe_utilities analysis_subprocess diff --git a/docs/source/api/lyse_functions.rst b/docs/source/api/lyse_functions.rst index 8601831..c8d06b5 100644 --- a/docs/source/api/lyse_functions.rst +++ b/docs/source/api/lyse_functions.rst @@ -4,4 +4,4 @@ Lyse Helper Functions .. automodule:: lyse :members: :undoc-members: - :exclude-members: Run, Sequence + :exclude-members: Shot, Run, Sequence diff --git a/docs/source/api/run.rst b/docs/source/api/shot.rst similarity index 55% rename from docs/source/api/run.rst rename to docs/source/api/shot.rst index 2d2f5e8..5bfb1b3 100644 --- a/docs/source/api/run.rst +++ b/docs/source/api/shot.rst @@ -1,10 +1,10 @@ -Run +Shot ========== -.. autoclass:: lyse.Run +.. autoclass:: lyse.Shot :members: :undoc-members: :inherited-members: :show-inheritance: - .. automethod:: lyse.Run.__init__ \ No newline at end of file + .. automethod:: lyse.Shot.__init__ \ No newline at end of file diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 90d5770..924eed1 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -24,11 +24,11 @@ An analysis on a single shot # Image attributes are also stored in this series: w_x2 = ser['side','absorption','OD','Gaussian_XW'] - # If we want actual measurement data, we'll have to instantiate a Run object: - run = Run(path) + # If we want actual measurement data, we'll have to instantiate a Shot object: + shot = Shot(path) # Obtaining a trace: - t, mot_fluorecence = run.get_trace('mot fluorecence') + t, mot_fluorecence = shot.get_trace('mot fluorecence') # Now we might do some analysis on this data. Say we've written a # linear fit function (or we're calling some other libaries linear @@ -51,7 +51,7 @@ An analysis on a single shot # We might wish to save this result so that we can compare it across # shots in a multishot analysis: - run.save_result('mot loadrate', c) + shot.save_result('mot loadrate', c) An analysis on multiple shots diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index eac0dce..05b5524 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -3,7 +3,7 @@ Introduction **Lyse** is a data analysis system which gets *your code* running on experimental data as it is acquired. It is fundamenally based around the ideas of experimental *shots* and analysis *routines*. A shot is one trial of an experiment, and a routine is a ``Python`` script, written by you, that does something with the measurement data from one or more shots. -Analysis routines can be either *single-shot* or *multi-shot*. This determines what data and functions are available to your code when it runs. A single-shot routine has access to the data from only one shot, and functions available for saving results only to the hdf5 file for that shot. A a multi-shot routine has access to the entire dataset from all the runs that are currently loaded into **lyse**, and has functions available for saving results to an hdf5 file which does not belong to any of the shots---it's a file that exists only to save the "meta results". +Analysis routines can be either *single-shot* or *multi-shot*. This determines what data and functions are available to your code when it runs. A single-shot routine has access to the data from only one shot, and functions available for saving results only to the hdf5 file for that shot. A a multi-shot routine has access to the entire dataset from all the shots that are currently loaded into **lyse**, and has functions available for saving results to an hdf5 file which does not belong to any of the shots---it's a file that exists only to save the "meta results". Actually things are far less magical than that. The only enforced difference between a single shot routine and a multi-shot routine is a single variable provided to your code when **lyse** runs it. Your code runs in a perfectly clean ``Python`` environment with this one exception: a variable in the global namespace called ``path``, which is a path to an hdf5 file. If you have told **lyse** that your routine is a singleshot one, then this path will point to the hdf5 file for the current shot being analysed. On the other hand, if you've told **lyse** that your routine is a multishot one, then it will be the path to an h5 file that has been selected in **lyse** for saving results to. diff --git a/lyse/__init__.py b/lyse/__init__.py index c2c249f..3c8fbd0 100644 --- a/lyse/__init__.py +++ b/lyse/__init__.py @@ -711,7 +711,11 @@ def globals_diff(self, other_shot, group=None): class Run(Shot): def __init__(self, *args, **kwargs): - warnings.warn("The 'Run' class has been renamed to 'Shot' (but is otherwise compatible). 'Run' will be removed in lyse v4.0. Please update your analysis code to use the 'Shot` class", DeprecationWarning) + msg = """The 'Run' class has been renamed to 'Shot' (but is otherwise + compatible). 'Run' will be removed in lyse v4.0. Please update your + analysis code to use the 'Shot' class. + """ + warnings.warn(dedent(msg), DeprecationWarning) super().__init__(*args, **kwargs) def globals_diff(self, other_run, group=None): @@ -741,9 +745,14 @@ def __init__(self, h5_path, shot_paths, no_write=False): @property def runs(self): - warnings.warn("The 'runs' attribute has been renamed to 'shots'. 'runs' will be removed in lyse v4.0. Please update your analysis code to use the 'shots` attribute", DeprecationWarning) + msg = """The 'runs' attribute has been renamed to 'shots'. 'runs' will be + removed in lyse v4.0. Please update your analysis code to use the 'shots' + attribute. + """ + warnings.warn(dedent(msg), DeprecationWarning) return copy.copy(self.__shots) + @property def shots(self): return copy.copy(self.__shots) From 95e10685c2ab53afe59f76d0dcfcd43a7d04b629 Mon Sep 17 00:00:00 2001 From: Phil Starkey Date: Sun, 8 Nov 2020 12:24:52 +1100 Subject: [PATCH 3/3] Added docstrings to `Sequence.shots` and `Sequence.runs` to reflect deprecation status. --- lyse/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lyse/__init__.py b/lyse/__init__.py index 3c8fbd0..97956b2 100644 --- a/lyse/__init__.py +++ b/lyse/__init__.py @@ -745,6 +745,10 @@ def __init__(self, h5_path, shot_paths, no_write=False): @property def runs(self): + """This property is deprecated and will be removed in lyse v4.0 + + Use the :attr:`shots` attribute instead. + """ msg = """The 'runs' attribute has been renamed to 'shots'. 'runs' will be removed in lyse v4.0. Please update your analysis code to use the 'shots' attribute. @@ -754,6 +758,10 @@ def runs(self): @property def shots(self): + """A dictionary containing :class:`Shot` instances. + + The dictionary is keyed by filepaths specified in the :code:`shot_paths` + argument at instantiation time.""" return copy.copy(self.__shots) def get_trace(self,*args):