Skip to content

Commit 4eef798

Browse files
authored
Merge pull request #119 from spielman-group/refactor
Initial refactoring
2 parents de8eb4d + f1e3dc7 commit 4eef798

20 files changed

Lines changed: 2303 additions & 2058 deletions

docs/source/api/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ API Reference
99

1010
lyse
1111
lyse.analysis_subprocess
12+
lyse.communication
1213
lyse.dataframe_utilities
1314
lyse.figure_manager
15+
lyse.filebox
16+
lyse.routines
1417
lyse.tempfile2clipboard
18+
lyse.utils
1519
lyse.__main__

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# -- Project information (unique to each project) -------------------------------------
1919

2020
project = "lyse"
21-
copyright = "2020, labscript suite"
21+
copyright = "2024, labscript suite"
2222
author = "labscript suite contributors"
2323

2424
# The full version, including alpha/beta/rc tags

lyse/__init__.py

Lines changed: 26 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -13,60 +13,43 @@
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
2016
import pickle as pickle
2117
from pathlib import Path
2218
import sys
23-
import threading
2419
import functools
2520
import contextlib
21+
import warnings
2622

2723
import labscript_utils.h5_lock, h5py
28-
from labscript_utils.labconfig import LabConfig
2924
import pandas
30-
from numpy import array, ndarray, where
31-
import types
25+
from numpy import array, where
3226

3327
from .__version__ import __version__
3428

3529
from labscript_utils import dedent
3630
from labscript_utils.ls_zprocess import zmq_get
31+
from labscript_utils.dict_diff import dict_diff
3732

3833
from 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

7154
class _RoutineStorage(object):
7255
"""An empty object that analysis routines can store data in. It will
@@ -80,7 +63,7 @@ class _RoutineStorage(object):
8063
routine_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-
210167
def 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

Comments
 (0)