2020 ~pairwise
2121 ~print_snapshot_list
2222 ~print_RE_md
23+ ~replay
2324 ~run_in_thread
2425 ~show_ophyd_symbols
2526 ~split_quoted_line
4041# The full license is in the file LICENSE.txt, distributed with this software.
4142#-----------------------------------------------------------------------------
4243
44+ from bluesky .callbacks .best_effort import BestEffortCallback
4345from collections import OrderedDict
46+ import databroker
4447import datetime
4548from email .mime .text import MIMEText
4649from event_model import NumpyEncoder
@@ -190,10 +193,12 @@ def list_recent_scans(num=20, keys=[], printing=True, show_command=False, db=Non
190193
191194 PARAMETERS
192195
193- num : int (default: ``20``)
196+ num : int
194197 Make the table include the ``num`` most recent scans.
195- keys : [str] (default: ``[]``)
198+ (default: ``20``)
199+ keys : [str]
196200 Include these additional keys from the start document.
201+ (default: ``[]``)
197202
198203 Two special keys are supported:
199204
@@ -203,13 +208,16 @@ def list_recent_scans(num=20, keys=[], printing=True, show_command=False, db=Non
203208 Also, it will be truncated so that it is no more than 40 characters.)
204209 * ``exit_status`` : from the stop document
205210
206- printing : bool (default: ``True``)
211+ printing : bool
207212 If True, print the table to stdout
208- show_command : bool (default: ``False``)
213+ (default: ``True``)
214+ show_command : bool
209215 If True, show the (reconstructed) full command,
210216 but truncate it to no more than 40 characters)
211- db : object (default: ``db`` from the IPython shell)
217+ (default: ``False``)
218+ db : object
212219 Instance of ``databroker.Broker()``
220+ (default: ``db`` from the IPython shell)
213221
214222 RETURNS
215223
@@ -229,13 +237,9 @@ def list_recent_scans(num=20, keys=[], printing=True, show_command=False, db=Non
229237 f17f026 2019-07-25 16:19:04.929030 149 count testing 4845
230238 ========= ========================== ======= ========= =========== =====
231239
240+ *new in apstools release 1.1.10*
232241 """
233- try :
234- from IPython import get_ipython
235- global_db = get_ipython ().user_ns ["db" ]
236- except AttributeError as _exc :
237- global_db = None
238- db = db or global_db
242+ db = db or ipython_shell_namespace ()["db" ]
239243
240244 if show_command :
241245 labels = "scan_id command" .split () + keys
@@ -299,12 +303,7 @@ def print_RE_md(dictionary=None, fmt="simple", printing=True):
299303 ======================== ===================================
300304
301305 """
302- try :
303- from IPython import get_ipython
304- RE = get_ipython ().user_ns ["RE" ]
305- except AttributeError as _exc :
306- RE = None
307- dictionary = dictionary or RE .md
306+ dictionary = dictionary or ipython_shell_namespace ()["RE" ].md
308307 md = dict (dictionary ) # copy of input for editing
309308 v = dictionary_table (md ["versions" ], fmt = fmt ) # sub-table
310309 md ["versions" ] = str (v ).rstrip ()
@@ -335,6 +334,42 @@ def pairwise(iterable):
335334 return zip (a , a )
336335
337336
337+ def replay (headers , callback = None ):
338+ """
339+ replay the document stream from one (or more) scans (headers)
340+
341+ PARAMETERS
342+
343+ headers: scan or [scan]
344+ Scan(s) to be replayed through callback.
345+ A *scan* is an instance of a Bluesky `databroker.Header`.
346+ see: https://nsls-ii.github.io/databroker/api.html?highlight=header#header-api
347+
348+ callback: scan or [scan]
349+ The Bluesky callback to handle the stream of documents from a scan.
350+ If `None`, then use the `bec` (BestEffortCallback) from the IPython shell.
351+ (default:`None`)
352+
353+ *new in apstools release 1.1.11*
354+ """
355+ callback = callback or ipython_shell_namespace ().get (
356+ "bec" , # get from IPython shell
357+ BestEffortCallback (), # make one, if we must
358+ )
359+ if isinstance (headers , databroker .Header ):
360+ headers = tuple (headers )
361+ for h in headers :
362+ if not isinstance (h , databroker .Header ):
363+ emsg = f"Must be a databroker Header: received: { type (h )} : |{ h } |"
364+ raise TypeError (emsg )
365+ cmd = _rebuild_scan_command (h .start )
366+ logger .debug (f"{ cmd } " )
367+
368+ # at last, this is where the real action happens
369+ for k , doc in h .documents (): # get the stream
370+ callback (k , doc ) # play it through the callback
371+
372+
338373def run_in_thread (func ):
339374 """
340375 (decorator) run ``func`` in thread
@@ -364,15 +399,19 @@ def show_ophyd_symbols(show_pv=True, printing=True, verbose=False, symbols=None)
364399
365400 PARAMETERS
366401
367- show_pv: bool (default: True)
402+ show_pv: bool
368403 If True, also show relevant EPICS PV, if available.
369- printing: bool (default: True)
404+ (default: True)
405+ printing: bool
370406 If True, print table to stdout.
371- verbose: bool (default: False)
407+ (default: True)
408+ verbose: bool
372409 If True, also show ``str(obj``.
373- symbols: dict (default: `globals()`)
410+ (default: False)
411+ symbols: dict
374412 If None, use global symbol table.
375413 If not None, use provided dictionary.
414+ (default: `globals()`)
376415
377416 RETURNS
378417
@@ -402,6 +441,8 @@ def show_ophyd_symbols(show_pv=True, printing=True, verbose=False, symbols=None)
402441 Out[1]: <pyRestTable.rest_table.Table at 0x7fa4398c7cf8>
403442
404443 In [2]:
444+
445+ *new in apstools release 1.1.8*
405446 """
406447 table = pyRestTable .Table ()
407448 table .labels = ["name" , "ophyd structure" ]
@@ -410,12 +451,14 @@ def show_ophyd_symbols(show_pv=True, printing=True, verbose=False, symbols=None)
410451 if verbose :
411452 table .addLabel ("object representation" )
412453 table .addLabel ("label(s)" )
413- try :
414- from IPython import get_ipython
415- g = get_ipython ().user_ns
416- except AttributeError as _exc :
417- g = globals ()
418- g = symbols or g
454+ if symbols is None :
455+ # the default choice
456+ g = ipython_shell_namespace ()
457+ if len (g ) == 0 :
458+ # ultimate fallback
459+ g = globals ()
460+ else :
461+ g = symbols
419462 for k , v in sorted (g .items ()):
420463 if isinstance (v , (ophyd .Signal , ophyd .Device )):
421464 row = [k , v .__class__ .__name__ ]
@@ -846,6 +889,18 @@ def ipython_profile_name():
846889 return get_ipython ().profile
847890
848891
892+ def ipython_shell_namespace ():
893+ """
894+ get the IPython shell's namespace dictionary (or empty if not found)
895+ """
896+ try :
897+ from IPython import get_ipython
898+ ns = get_ipython ().user_ns
899+ except AttributeError as _exc :
900+ ns = {}
901+ return ns
902+
903+
849904def print_snapshot_list (db , ** search_criteria ):
850905 """
851906 print (stdout) a list of all snapshots in the databroker
0 commit comments