def test_finalize(): def plan(): yield from [Msg('null')] def cleanup_plan(): yield from [Msg('read', det)] # wrapper accepts list processed_plan = list(finalize_wrapper(plan(), [Msg('read', det)])) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or func that returns list def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), lambda: [Msg('read', det)])) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or generator instance def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), cleanup_plan())) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or generator func def plan(): yield from [Msg('null')] processed_plan = list(finalize_wrapper(plan(), cleanup_plan)) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # decorator accepts generator func processed_plan = list(finalize_decorator(cleanup_plan)(plan)()) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # or func that returns list processed_plan = list( finalize_decorator(lambda: [Msg('read', det)])(plan)()) expected = [Msg('null'), Msg('read', det)] assert processed_plan == expected # decorator does NOT accept list with pytest.raises(TypeError): list(finalize_decorator([Msg('read', det)])(plan)()) # nor generator instance with pytest.raises(TypeError): list(finalize_decorator(cleanup_plan())(plan)())
def test_finalize(): def plan(): yield from [Msg("null")] def cleanup_plan(): yield from [Msg("read", det)] # wrapper accepts list processed_plan = list(finalize_wrapper(plan(), [Msg("read", det)])) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # or func that returns list def plan(): yield from [Msg("null")] processed_plan = list(finalize_wrapper(plan(), lambda: [Msg("read", det)])) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # or generator instance def plan(): yield from [Msg("null")] processed_plan = list(finalize_wrapper(plan(), cleanup_plan())) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # or generator func def plan(): yield from [Msg("null")] processed_plan = list(finalize_wrapper(plan(), cleanup_plan)) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # decorator accepts generator func processed_plan = list(finalize_decorator(cleanup_plan)(plan)()) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # or func that returns list processed_plan = list(finalize_decorator(lambda: [Msg("read", det)])(plan)()) expected = [Msg("null"), Msg("read", det)] assert processed_plan == expected # decorator does NOT accept list with pytest.raises(TypeError): list(finalize_decorator([Msg("read", det)])(plan)()) # nor generator instance with pytest.raises(TypeError): list(finalize_decorator(cleanup_plan())(plan)())
def test_sigint_three_hits(fresh_RE, motor_det): motor, det = motor_det motor._fake_sleep = 0.3 RE = fresh_RE pid = os.getpid() def sim_kill(n=1): for j in range(n): print('KILL') os.kill(pid, signal.SIGINT) lp = RE.loop motor.loop = lp lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) start_time = ttime.time() RE( bp.finalize_wrapper(bp.abs_set(motor, 1, wait=True), bp.abs_set(motor, 0, wait=True))) end_time = ttime.time() assert end_time - start_time < 0.2 # not enough time for motor to cleanup RE.abort() # now cleanup done_cleanup_time = ttime.time() assert done_cleanup_time - end_time > 0.3
def test_sigint_manyhits(fresh_RE, motor_det): motor, det = motor_det motor._fake_sleep = 0.3 RE = fresh_RE pid = os.getpid() def sim_kill(n=1): for j in range(n): print('KILL') os.kill(pid, signal.SIGINT) lp = RE.loop motor.loop = lp lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) lp.call_later(.02, sim_kill, 3) start_time = ttime.time() RE(bp.finalize_wrapper(bp.abs_set(motor, 1, wait=True), bp.abs_set(motor, 0, wait=True))) end_time = ttime.time() assert end_time - start_time < 0.2 # not enough time for motor to cleanup RE.abort() # now cleanup done_cleanup_time = ttime.time() assert done_cleanup_time - end_time > 0.3
def test_finialize_pause(): fail_cmd = 'fail_next' def erroring_plan(): yield Msg(fail_cmd, None) raise RuntimeError('saw this coming') num = 5 cmd = 'echo' plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num), pause_for_debug=True) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 2 _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd, 'pause'] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}, { 'defer': False }] + [{}] * num)
def test_finalizer_closeable(): pre = (j for j in range(18)) post = (j for j in range(18)) plan = bp.finalize_wrapper(pre, post) for j in range(3): next(plan) plan.close()
def test_finialize_success(): suc_cmd = "it_works" num = 5 cmd = "echo" plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[suc_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
def configure_count_time_wrapper(plan, time): """ Preprocessor that sets all devices with a `count_time` to the same time. The original setting is stashed and restored at the end. Parameters ---------- plan : iterable or iterator a generator, list, or similar containing `Msg` objects time : float or None If None, the plan passes through unchanged. Yields ------ msg : Msg messages from plan, with 'set' messages inserted """ devices_seen = set() original_times = {} def insert_set(msg): obj = msg.obj if obj is not None and obj not in devices_seen: devices_seen.add(obj) if hasattr(obj, 'count_time'): # TODO Do this with a 'read' Msg once reads can be # marked as belonging to a different event stream (or no # event stream. original_times[obj] = obj.count_time.get() # TODO do this with configure return pchain(mv(obj.count_time, time), single_gen(msg)), None return None, None def reset(): for obj, time in original_times.items(): yield from mv(obj.count_time, time) if time is None: # no-op return (yield from plan) else: return (yield from finalize_wrapper(plan_mutator(plan, insert_set), reset()))
def test_finialize_fail(): fail_cmd = "fail_next" def erroring_plan(): yield Msg(fail_cmd, None) raise RuntimeError("saw this coming") num = 5 cmd = "echo" plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
def test_finalize_runs_after_error(fresh_RE): def plan(): yield from [Msg('null')] raise Exception msgs = [] def accumulator(msg): msgs.append(msg) fresh_RE.msg_hook = accumulator try: fresh_RE(finalize_wrapper(plan(), [Msg('read', det)])) except Exception: pass # swallow the Exception; we are interested in msgs below expected = [Msg('null'), Msg('read', det)] assert msgs == expected
def test_finalize_runs_after_error(fresh_RE): def plan(): yield from [Msg("null")] raise Exception msgs = [] def accumulator(msg): msgs.append(msg) fresh_RE.msg_hook = accumulator try: fresh_RE(finalize_wrapper(plan(), [Msg("read", det)])) except Exception: pass # swallow the Exception; we are interested in msgs below expected = [Msg("null"), Msg("read", det)] assert msgs == expected
def test_finialize_success(): suc_cmd = 'it_works' num = 5 cmd = 'echo' plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)), echo_plan(command=cmd, num=num)) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 1 _verify_msg_seq(msgs, m_len=total, cmd_sq=[suc_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
def test_finialize_pause(): fail_cmd = 'fail_next' def erroring_plan(): yield Msg(fail_cmd, None) raise RuntimeError('saw this coming') num = 5 cmd = 'echo' plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num), pause_for_debug=True) msgs = list() try: EchoRE(plan, msg_list=msgs) except RuntimeError: pass total = num + 2 _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd, 'pause'] + [cmd]*num, args_sq=[()]*total, kwargs_sq=[{}, {'defer': False}] + [{}]*num)
def fast_shutter_wrapper(plan): if gs.USE_FAST_SHUTTER: plan = bsp.pchain(bsp.abs_set(fast_shutter.output, FastShutter.OPEN_SHUTTER, settle_time=FastShutter.SETTLE_TIME), plan) plan = bsp.finalize_wrapper(plan, bsp.abs_set(fast_shutter.output, FastShutter.CLOSE_SHUTTER, settle_time=FastShutter.SETTLE_TIME)) return (yield from plan)
def __call__(self, sample, plan, subs=None, *, verify_write=False, dark_strategy=periodic_dark, raise_if_interrupted=False, **metadata_kw): # The CustomizedRunEngine knows about a Beamtime object, and it # interprets integers for 'sample' as indexes into the Beamtime's # lists of Samples from all its Experiments. # deprecated from v0.5 release #if getattr(glbl, 'collection', None) is None: # raise RuntimeError("No collection has been linked to current " # "experiment yet.\nPlease do\n" # ">>> open_collection(<collection_name>)\n" # "before you run any xrun") if isinstance(sample, int): try: sample = self.beamtime.samples[sample] except IndexError: print("WARNING: hmm, there is no sample with index `{}`" ", please do `bt.list()` to check if it exists yet" .format(sample)) return # If a plan is given as a string, look in up in the global registry. if isinstance(plan, int): try: plan = self.beamtime.scanplans[plan] except IndexError: print("WARNING: hmm, there is no scanplan with index `{}`" ", please do `bt.list()` to check if it exists yet" .format(plan)) return # If the plan is an xpdAcq 'ScanPlan', make the actual plan. if isinstance(plan, ScanPlan): plan = plan.factory() _subs = normalize_subs_input(subs) if verify_write: _subs.update({'stop': verify_files_saved}) # No keys in metadata_kw are allows to collide with sample keys. if set(sample) & set(metadata_kw): raise ValueError("These keys in metadata_kw are illegal " "because they are always in sample: " "{}".format(set(sample) & set(metadata_kw))) if self._beamtime.get('bt_wavelength') is None: print("WARNING: there is no wavelength information in current" "beamtime object, scan will keep going....") metadata_kw.update(sample) sh = glbl.shutter if glbl.shutter_control: # Alter the plan to incorporate dark frames. # only works if user allows shutter control if glbl.auto_dark: plan = dark_strategy(plan) plan = bp.msg_mutator(plan, _inject_qualified_dark_frame_uid) # force to close shutter after scan plan = bp.finalize_wrapper(plan, bp.abs_set(sh, 0, wait=True)) # Load calibration file if glbl.auto_load_calib: plan = bp.msg_mutator(plan, _inject_calibration_md) # Insert glbl mask plan = bp.msg_mutator(plan, _inject_mask) # Execute return super().__call__(plan, subs, raise_if_interrupted=raise_if_interrupted, **metadata_kw)
def __call__(self, sample, plan, subs=None, *, verify_write=False, dark_strategy=periodic_dark, raise_if_interrupted=False, **metadata_kw): # The CustomizedRunEngine knows about a Beamtime object, and it # interprets integers for 'sample' as indexes into the Beamtime's # lists of Samples from all its Experiments. # deprecated from v0.5 release #if getattr(glbl, 'collection', None) is None: # raise RuntimeError("No collection has been linked to current " # "experiment yet.\nPlease do\n" # ">>> open_collection(<collection_name>)\n" # "before you run any xrun") if isinstance(sample, int): try: sample = self.beamtime.samples[sample] except IndexError: print( "WARNING: hmm, there is no sample with index `{}`" ", please do `bt.list()` to check if it exists yet".format( sample)) return # If a plan is given as a string, look in up in the global registry. if isinstance(plan, int): try: plan = self.beamtime.scanplans[plan] except IndexError: print( "WARNING: hmm, there is no scanplan with index `{}`" ", please do `bt.list()` to check if it exists yet".format( plan)) return # If the plan is an xpdAcq 'ScanPlan', make the actual plan. if isinstance(plan, ScanPlan): plan = plan.factory() _subs = normalize_subs_input(subs) if verify_write: _subs.update({'stop': verify_files_saved}) # No keys in metadata_kw are allows to collide with sample keys. if set(sample) & set(metadata_kw): raise ValueError("These keys in metadata_kw are illegal " "because they are always in sample: " "{}".format(set(sample) & set(metadata_kw))) if self._beamtime.get('bt_wavelength') is None: print("WARNING: there is no wavelength information in current" "beamtime object, scan will keep going....") metadata_kw.update(sample) sh = glbl.shutter if glbl.shutter_control: # Alter the plan to incorporate dark frames. # only works if user allows shutter control if glbl.auto_dark: plan = dark_strategy(plan) plan = bp.msg_mutator(plan, _inject_qualified_dark_frame_uid) # force to close shutter after scan plan = bp.finalize_wrapper(plan, bp.abs_set(sh, 0, wait=True)) # Load calibration file if glbl.auto_load_calib: plan = bp.msg_mutator(plan, _inject_calibration_md) # Insert glbl mask plan = bp.msg_mutator(plan, _inject_mask) # Execute return super().__call__(plan, subs, raise_if_interrupted=raise_if_interrupted, **metadata_kw)