1313"""Lyse analysis API
1414"""
1515
16- from lyse .dataframe_utilities import get_series_from_shot as _get_singleshot
17- from labscript_utils .dict_diff import dict_diff
18- import os
19- import socket
2016import pickle as pickle
2117from pathlib import Path
2218import sys
23- import threading
2419import functools
2520import contextlib
21+ import warnings
2622
2723import labscript_utils .h5_lock , h5py
28- from labscript_utils .labconfig import LabConfig
2924import pandas
30- from numpy import array , ndarray , where
31- import types
25+ from numpy import array , where
3226
3327from .__version__ import __version__
3428
3529from labscript_utils import dedent
3630from labscript_utils .ls_zprocess import zmq_get
31+ from labscript_utils .dict_diff import dict_diff
3732
3833from labscript_utils .properties import get_attributes , get_attribute , set_attributes
39- LYSE_DIR = os .path .dirname (os .path .realpath (__file__ ))
40-
41- # If running stand-alone, and not from within lyse, the below two variables
42- # will be as follows. Otherwise lyse will override them with spinning_top =
43- # True and path <name of hdf5 file being analysed>:
44- spinning_top = False
45- # data to be sent back to the lyse GUI if running within lyse
46- _updated_data = {}
47- # dictionary of plot id's to classes to use for Plot object
48- _plot_classes = {}
49- # A fake Plot object to subclass if we are not running in the GUI
50- Plot = object
51- # An empty dictionary of plots (overwritten by the analysis worker if running within lyse)
52- plots = {}
53- # A threading.Event to delay the
54- delay_event = threading .Event ()
55- # a flag to determine whether we should wait for the delay event
56- _delay_flag = False
57-
58- # get port that lyse is using for communication
59- try :
60- _labconfig = LabConfig (required_params = {"ports" : ["lyse" ]})
61- _lyse_port = int (_labconfig .get ('ports' , 'lyse' ))
62- except Exception :
63- _lyse_port = 42519
64-
65- if len (sys .argv ) > 1 :
66- path = sys .argv [1 ]
67- else :
68- path = None
6934
35+ # lyse imports
36+ import lyse .dataframe_utilities
37+ import lyse .utils
38+
39+ # Import this way so LYSE_DIR is exposed when someone does import lyse or from lyse import *
40+ from lyse .utils import LYSE_DIR
41+ from lyse .utils .worker import spinning_top , _updated_data , register_plot_class , delay_results_return
42+
43+ # note: path is injected into the script namespace by AnalysisWorker at runtime
44+ def __getattr__ (name ):
45+ if name == 'path' :
46+ from lyse .utils .worker import path
47+ warnings .warn ("'path' is now automatically injected into the script namespace. "
48+ "Importing it from 'lyse.path' will be deprecated." ,
49+ FutureWarning )
50+ return path
51+ else :
52+ raise AttributeError (f"module '{ __name__ } ' has no attribute '{ name } '" )
7053
7154class _RoutineStorage (object ):
7255 """An empty object that analysis routines can store data in. It will
@@ -80,7 +63,7 @@ class _RoutineStorage(object):
8063routine_storage = _RoutineStorage ()
8164
8265
83- def data (filepath = None , host = 'localhost' , port = _lyse_port , timeout = 5 , n_sequences = None , filter_kwargs = None ):
66+ def data (filepath = None , host = 'localhost' , port = lyse . utils . LYSE_PORT , timeout = 5 , n_sequences = None , filter_kwargs = None ):
8467 """Get data from the lyse dataframe or a file.
8568
8669 This function allows for either extracting information from a run's hdf5
@@ -146,7 +129,7 @@ def data(filepath=None, host='localhost', port=_lyse_port, timeout=5, n_sequence
146129 the lyse dataframe, or a subset of it, is returned.
147130 """
148131 if filepath is not None :
149- return _get_singleshot (filepath )
132+ return lyse . dataframe_utilities . get_series_from_shot (filepath )
150133 else :
151134 if n_sequences is not None :
152135 if not (type (n_sequences ) is int and n_sequences >= 0 ):
@@ -161,7 +144,7 @@ def data(filepath=None, host='localhost', port=_lyse_port, timeout=5, n_sequence
161144
162145 # Allow sending 'get dataframe' (without the enclosing list) if
163146 # n_sequences and filter_kwargs aren't provided. This is for backwards
164- # compatability in case the server is running an outdated version of
147+ # compatibility in case the server is running an outdated version of
165148 # lyse.
166149 if n_sequences is None and filter_kwargs is None :
167150 command = 'get dataframe'
@@ -178,35 +161,9 @@ def data(filepath=None, host='localhost', port=_lyse_port, timeout=5, n_sequence
178161 raise ValueError (dedent (msg ))
179162 # Ensure conversion to multiindex is done, which needs to be done here
180163 # if the server is running an outdated version of lyse.
181- _rangeindex_to_multiindex (df , inplace = True )
164+ lyse . dataframe_utilities . rangeindex_to_multiindex (df , inplace = True )
182165 return df
183166
184- def _rangeindex_to_multiindex (df , inplace ):
185- if isinstance (df .index , pandas .MultiIndex ):
186- # The dataframe has already been converted.
187- return df
188- try :
189- padding = ('' ,)* (df .columns .nlevels - 1 )
190- try :
191- integer_indexing = _labconfig .getboolean ('lyse' , 'integer_indexing' )
192- except (LabConfig .NoOptionError , LabConfig .NoSectionError ):
193- integer_indexing = False
194- if integer_indexing :
195- out = df .set_index (['sequence_index' , 'run number' , 'run repeat' ], inplace = inplace , drop = False )
196- # out is None if inplace is True, and is the new dataframe is inplace is False.
197- if not inplace :
198- df = out
199- else :
200- out = df .set_index ([('sequence' ,) + padding ,('run time' ,) + padding ], inplace = inplace , drop = False )
201- if not inplace :
202- df = out
203- df .index .names = ['sequence' , 'run time' ]
204- except KeyError :
205- # Empty DataFrame or index column not found, so fall back to RangeIndex instead
206- pass
207- df .sort_index (inplace = True )
208- return df
209-
210167def globals_diff (run1 , run2 , group = None ):
211168 """Take a diff of the globals between two runs.
212169
@@ -674,6 +631,7 @@ def save_result(self, name, value, group=None, overwrite=True):
674631 set_attributes (self .h5_file [group ], {name : value })
675632
676633 if spinning_top :
634+ global _updated_data
677635 if self .h5_path not in _updated_data :
678636 _updated_data [self .h5_path ] = {}
679637 if group .startswith ('results' ):
@@ -1200,58 +1158,3 @@ def get_image(self,*args):
12001158 Not implemented, but could be.
12011159 """
12021160 raise NotImplementedError ('If you want to use this feature please ask me to implement it! -Chris' )
1203-
1204-
1205- def figure_to_clipboard (figure = None , ** kwargs ):
1206- """Copy a matplotlib figure to the clipboard as a png.
1207-
1208- If figure is None,
1209- the current figure will be copied. Copying the figure is implemented by
1210- calling figure.savefig() and then copying the image data from the
1211- resulting file. If bbox_inches keyword arg is not provided,
1212- bbox_inches='tight' will be used.
1213-
1214- Args:
1215- figure (:obj:`matplotlib:matplotlib.figure`, optional):
1216- Figure to copy to the clipboard. If `None`, copies the current figure.
1217- **kwargs: Passed to `figure.savefig()` as kwargs.
1218- """
1219-
1220- import matplotlib .pyplot as plt
1221- from zprocess import start_daemon
1222- import tempfile
1223-
1224- if 'bbox_inches' not in kwargs :
1225- kwargs ['bbox_inches' ] = 'tight'
1226-
1227- if figure is None :
1228- figure = plt .gcf ()
1229-
1230- with tempfile .NamedTemporaryFile (suffix = '.png' , delete = False ) as f :
1231- tempfile_name = f .name
1232-
1233- figure .savefig (tempfile_name , ** kwargs )
1234-
1235- tempfile2clipboard = os .path .join (LYSE_DIR , 'tempfile2clipboard.py' )
1236- start_daemon ([sys .executable , tempfile2clipboard , '--delete' , tempfile_name ])
1237-
1238-
1239- def register_plot_class (identifier , cls ):
1240- if not spinning_top :
1241- msg = """Warning: lyse.register_plot_class has no effect on scripts not run with
1242- the lyse GUI.
1243- """
1244- sys .stderr .write (dedent (msg ))
1245- _plot_classes [identifier ] = cls
1246-
1247- def get_plot_class (identifier ):
1248- return _plot_classes .get (identifier , None )
1249-
1250- def delay_results_return ():
1251- global _delay_flag
1252- if not spinning_top :
1253- msg = """Warning: lyse.delay_results_return has no effect on scripts not run
1254- with the lyse GUI.
1255- """
1256- sys .stderr .write (dedent (msg ))
1257- _delay_flag = True
0 commit comments