From 9f2e67ea9e265d46727a92c87f12ed5ea98b0a54 Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 13 Jun 2025 14:27:11 -0400 Subject: [PATCH 01/32] WIP Merlin integration --- src/cditools/merlin.py | 145 +++++++++++++++++++++++++++++++++ src/cditools/trigger_mixins.py | 103 +++++++++++++++++++++++ src/cditools/utils.py | 33 ++++++++ 3 files changed, 281 insertions(+) create mode 100644 src/cditools/merlin.py create mode 100644 src/cditools/trigger_mixins.py create mode 100644 src/cditools/utils.py diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py new file mode 100644 index 00000000..1cf13782 --- /dev/null +++ b/src/cditools/merlin.py @@ -0,0 +1,145 @@ +from __future__ import print_function +import logging +from pathlib import PurePath +from ophyd import (AreaDetector, CamBase, TIFFPlugin, Component as Cpt, + HDF5Plugin, Device, StatsPlugin, ProcessPlugin, + ROIPlugin, TransformPlugin, EpicsSignal) +from ophyd.areadetector import EpicsSignalWithRBV + +from ophyd.areadetector.base import ADComponent +from ophyd.areadetector.filestore_mixins import ( + FileStoreTIFF, FileStorePluginBase) + +from .utils import makedirs +from .trigger_mixins import (CDIModalTrigger, FileStoreBulkReadable) + + +logger = logging.getLogger(__name__) + + +class MerlinTiffPlugin(TIFFPlugin, FileStoreBulkReadable, FileStoreTIFF, + Device): + def mode_external(self): + total_points = self.parent.mode_settings.total_points.get() + self.stage_sigs[self.num_capture] = total_points + + def get_frames_per_point(self): + mode = self.parent.mode_settings.mode.get() + if mode == 'external': + return 1 + else: + return self.parent.cam.num_images.get() + + +class MerlinDetectorCam(CamBase): + acquire = ADComponent(EpicsSignal,'Acquire') + quad_merlin_mode = ADComponent(EpicsSignalWithRBV,'QuadMerlinMode') + pass + + +class MerlinDetector(AreaDetector): + cam = Cpt(MerlinDetectorCam, 'cam1:', + read_attrs=[], + configuration_attrs=['image_mode', 'trigger_mode', + 'acquire_time', 'acquire_period'], + ) + + +class MerlinFileStoreHDF5(FileStorePluginBase, FileStoreBulkReadable): + _spec = 'TPX_HDF5' + filestore_spec = _spec + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.stage_sigs.update([(self.file_template, '%s%s_%6.6d.h5'), + (self.file_write_mode, 'Stream'), + (self.compression, 'zlib'), + (self.capture, 1) + ]) + + def stage(self): + logger.info("Staging") + staged = super().stage() + logger.info("Staging step 2") + res_kwargs = {'frame_per_point': 1} + logger.info("res_kwargs = {frame_per_point: }") + + logger.debug("Inserting resource with filename %s", self._fn) + logger.info("Inserting resource with filename %s", self._fn) + self._generate_resource(res_kwargs) + logger.info("generating resources") + logger.info("Staged") + return staged + + def make_filename(self): + fn, read_path, write_path = super().make_filename() + mode_settings = self.parent.mode_settings + if mode_settings.make_directories.get(): + makedirs(read_path) + return fn, read_path, write_path + + +class HDF5PluginWithFileStore(HDF5Plugin, MerlinFileStoreHDF5): + def stage(self): + mode_settings = self.parent.mode_settings + total_points = mode_settings.total_points.get() + self.stage_sigs[self.num_capture] = total_points + + # ensure that setting capture is the last thing that's done + self.stage_sigs.move_to_end(self.capture) + return super().stage() + + +class CDIMerlinDetector(HxnModalTrigger, MerlinDetector): + hdf5 = Cpt(HDF5PluginWithFileStore, 'HDF1:', + read_attrs=[], + configuration_attrs=[], + write_path_template='/data/%Y/%m/%d/', + root='/data') + + proc1 = Cpt(ProcessPlugin, 'Proc1:') + stats1 = Cpt(StatsPlugin, 'Stats1:') + stats2 = Cpt(StatsPlugin, 'Stats2:') + stats3 = Cpt(StatsPlugin, 'Stats3:') + stats4 = Cpt(StatsPlugin, 'Stats4:') + stats5 = Cpt(StatsPlugin, 'Stats5:') + transform1 = Cpt(TransformPlugin, 'Trans1:') + roi1 = Cpt(ROIPlugin, 'ROI1:') + roi2 = Cpt(ROIPlugin, 'ROI2:') + roi3 = Cpt(ROIPlugin, 'ROI3:') + roi4 = Cpt(ROIPlugin, 'ROI4:') + + def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, + **kwargs): + if read_attrs is None: + read_attrs = ['hdf5', 'cam'] + if configuration_attrs is None: + configuration_attrs = ['hdf5', 'cam'] + + if 'hdf5' not in read_attrs: + # ensure that hdf5 is still added, or data acquisition will fail + read_attrs = list(read_attrs) + ['hdf5'] + + super().__init__(prefix, configuration_attrs=configuration_attrs, + read_attrs=read_attrs, **kwargs) + + def mode_internal(self): + super().mode_internal() + + count_time = self.count_time.get() + if count_time is not None: + self.stage_sigs[self.cam.acquire_time] = count_time + self.stage_sigs[self.cam.acquire_period] = count_time + 0.005 + + def mode_external(self): + super().mode_external() + + # NOTE: these values specify a debounce time for external triggering so + # they should be set to < 0.5 the expected exposure time, or at + # minimum the lowest possible dead time = 1.64ms + expected_exposure = 0.001 + min_dead_time = 0.00164 + self.stage_sigs[self.cam.acquire_time] = expected_exposure + self.stage_sigs[self.cam.acquire_period] = expected_exposure + min_dead_time + + self.cam.stage_sigs[self.cam.trigger_mode] = 'Trigger Enable' \ No newline at end of file diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py new file mode 100644 index 00000000..63b23637 --- /dev/null +++ b/src/cditools/trigger_mixins.py @@ -0,0 +1,103 @@ +class CDIModalTrigger(HxnModalBase, TriggerBase): + def __init__(self, *args, image_name=None, **kwargs): + super().__init__(*args, **kwargs) + if image_name is None: + image_name = '_'.join([self.name, 'image']) + self._image_name = image_name + self._external_acquire_at_stage = True + + def stop(self, success=False): + ret = super().stop(success=success) + self._acquisition_signal.put(0, wait=True) + return ret + + def mode_internal(self): + super().mode_internal() + + cam = self.cam + cam.stage_sigs[cam.acquire] = 0 + ordered_dict_move_to_beginning(cam.stage_sigs, cam.acquire) + + cam.stage_sigs[cam.num_images] = 1 + cam.stage_sigs[cam.image_mode] = 'Single' + cam.stage_sigs[cam.trigger_mode] = 'Internal' + + def mode_external(self): + super().mode_external() + total_points = self.mode_settings.total_points.get() + + cam = self.cam + cam.stage_sigs[cam.num_images] = total_points + cam.stage_sigs[cam.image_mode] = 'Multiple' + cam.stage_sigs[cam.trigger_mode] = 'External' + + def stage(self): + self._acquisition_signal.subscribe(self._acquire_changed) + staged = super().stage() + + # In external triggering mode, the devices is only triggered once at + # stage + if self.mode == 'external' and self._external_acquire_at_stage: + self._acquisition_signal.put(1, wait=False) + return staged + + def unstage(self): + try: + return super().unstage() + finally: + self._acquisition_signal.clear_sub(self._acquire_changed) + + def trigger_internal(self): + if self._staged != Staged.yes: + raise RuntimeError("This detector is not ready to trigger." + "Call the stage() method before triggering.") + + self._status = DeviceStatus(self) + self._acquisition_signal.put(1, wait=False) + self.dispatch(self._image_name, ttime.time()) + return self._status + + def trigger_external(self): + if self._staged != Staged.yes: + raise RuntimeError("This detector is not ready to trigger." + "Call the stage() method before triggering.") + + self._status = DeviceStatus(self) + self._status._finished() + # TODO this timestamp is inaccurate! + if self.mode_settings.scan_type.get() != 'fly': + # Don't dispatch images for fly-scans - they are bulk read at the end + self.dispatch(self._image_name, ttime.time()) + return self._status + + def trigger(self): + mode_trigger = getattr(self, f'trigger_{self.mode}') + return mode_trigger() + + def _acquire_changed(self, value=None, old_value=None, **kwargs): + '''This is called when the 'acquire' signal changes.''' + if self._status is None: + return + if (old_value == 1) and (value == 0): + # Negative-going edge means an acquisition just finished. + self._status._finished() + + +class FileStoreBulkReadable(FileStoreIterativeWrite): + + def _reset_data(self): + self._datum_uids.clear() + self._point_counter = itertools.count() + + def bulk_read(self, timestamps): + image_name = self.image_name + + uids = [self.generate_datum(self.image_name, ts, {}) for ts in timestamps] + + # clear so unstage will not save the images twice: + self._reset_data() + return {image_name: uids} + + @property + def image_name(self): + return self.parent._image_name \ No newline at end of file diff --git a/src/cditools/utils.py b/src/cditools/utils.py new file mode 100644 index 00000000..9126d4a6 --- /dev/null +++ b/src/cditools/utils.py @@ -0,0 +1,33 @@ +from __future__ import print_function +import os + + +def makedirs(path, mode=0o777): + '''Recursively make directories and set permissions''' + # Permissions not working with os.makedirs - + # See: http://stackoverflow.com/questions/5231901 + if not path or os.path.exists(path): + return [] + + head, tail = os.path.split(path) + ret = makedirs(head, mode) + try: + os.mkdir(path) + except OSError as ex: + if 'File exists' not in str(ex): + raise + + os.chmod(path, mode) + ret.append(path) + return ret + +def ordered_dict_move_to_beginning(od, key): + if key not in od: + return + + value = od[key] + items = list((k, v) for k, v in od.items() + if k != key) + od.clear() + od[key] = value + od.update(items) \ No newline at end of file From 57a4ae958f00f46cab48a4a0c6494b630df6b25a Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 17 Jun 2025 11:19:23 -0400 Subject: [PATCH 02/32] trigger mixins --- src/cditools/trigger_mixins.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 63b23637..ccb162ae 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -1,3 +1,18 @@ +import time as ttime +import itertools +import logging + +from ophyd.device import (DeviceStatus, BlueskyInterface, Staged, + Component as Cpt, Device) +from ophyd import (Signal, ) +from ophyd.areadetector.filestore_mixins import FileStoreIterativeWrite + + +from .utils import ordered_dict_move_to_beginning + +logger = logging.getLogger(__name__) + + class CDIModalTrigger(HxnModalBase, TriggerBase): def __init__(self, *args, image_name=None, **kwargs): super().__init__(*args, **kwargs) From 7e97dae8ae93874a9935adfe03d3ca2f8b6d9c93 Mon Sep 17 00:00:00 2001 From: jennmald Date: Mon, 23 Jun 2025 14:24:32 -0400 Subject: [PATCH 03/32] fix modalbase --- src/cditools/merlin.py | 2 +- src/cditools/trigger_mixins.py | 53 +++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 1cf13782..921450f2 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -90,7 +90,7 @@ def stage(self): return super().stage() -class CDIMerlinDetector(HxnModalTrigger, MerlinDetector): +class CDIMerlinDetector(CDIModalTrigger, MerlinDetector): hdf5 = Cpt(HDF5PluginWithFileStore, 'HDF1:', read_attrs=[], configuration_attrs=[], diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index ccb162ae..1d0fc0c3 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -12,8 +12,59 @@ logger = logging.getLogger(__name__) +class TriggerBase(BlueskyInterface): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # If acquiring, stop. + self.stage_sigs[self.cam.acquire] = 0 + self.stage_sigs[self.cam.image_mode] = 'Multiple' + self._acquisition_signal = self.cam.acquire + + self._status = None + +class CDIModalBase(Device): + mode_settings = Cpt(HxnModalSettings, '') + count_time = Cpt(Signal, value=1.0, + doc='Exposure/count time, as specified by bluesky') + + def mode_setup(self, mode): + devices = [self] + [getattr(self, attr) for attr in self._sub_devices] + attr = 'mode_{}'.format(mode) + for dev in devices: + if hasattr(dev, attr): + mode_setup_method = getattr(dev, attr) + mode_setup_method() + + def mode_internal(self): + logger.debug('%s internal triggering %s', self.name, + self.mode_settings.get()) + + def mode_external(self): + logger.debug('%s external triggering %s', self.name, + self.mode_settings.get()) + + @property + def mode(self): + '''Trigger mode (external/internal)''' + return self.mode_settings.mode.get() + + def stage(self): + if self._staged != Staged.yes: + self.mode_setup(self.mode) + + return super().stage() + + def unstage(self): + if self.mode == 'external': + logger.info('[Unstage] Stopping externally-triggered detector %s', + self.name) + self.stop(success=True) + + super().unstage() + -class CDIModalTrigger(HxnModalBase, TriggerBase): +class CDIModalTrigger(CDIModalBase, TriggerBase): def __init__(self, *args, image_name=None, **kwargs): super().__init__(*args, **kwargs) if image_name is None: From 97572467219407067aaef3a0c8f335187378278d Mon Sep 17 00:00:00 2001 From: jennmald Date: Mon, 23 Jun 2025 14:30:21 -0400 Subject: [PATCH 04/32] change name --- src/cditools/trigger_mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 1d0fc0c3..ace0a3b7 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -24,7 +24,7 @@ def __init__(self, *args, **kwargs): self._status = None class CDIModalBase(Device): - mode_settings = Cpt(HxnModalSettings, '') + mode_settings = Cpt(CDIModalSettings, '') count_time = Cpt(Signal, value=1.0, doc='Exposure/count time, as specified by bluesky') From c09d4a2a42a3502d7fee93db0dfba161025c88ce Mon Sep 17 00:00:00 2001 From: jennmald Date: Mon, 23 Jun 2025 14:44:37 -0400 Subject: [PATCH 05/32] CDIModalSettings --- src/cditools/trigger_mixins.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index ace0a3b7..26c9a526 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -23,6 +23,18 @@ def __init__(self, *args, **kwargs): self._status = None +class CDIModalSettings(Device): + mode = Cpt(Signal, value='internal', + doc='Triggering mode (internal/external)') + scan_type = Cpt(Signal, value='step', + doc='Scan type (step/fly)') + make_directories = Cpt(Signal, value=True, + doc='Make directories on the DAQ side') + total_points = Cpt(Signal, value=2, + doc='The total number of points to acquire overall') + triggers = Cpt(Signal, value=None, + doc='Detector instances which this one triggers') + class CDIModalBase(Device): mode_settings = Cpt(CDIModalSettings, '') count_time = Cpt(Signal, value=1.0, From 2ee5e77adda950943da36014e5a03bd06a36f99d Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 24 Jun 2025 10:48:59 -0400 Subject: [PATCH 06/32] fix file writing templates --- src/cditools/merlin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 921450f2..bab48113 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -94,8 +94,8 @@ class CDIMerlinDetector(CDIModalTrigger, MerlinDetector): hdf5 = Cpt(HDF5PluginWithFileStore, 'HDF1:', read_attrs=[], configuration_attrs=[], - write_path_template='/data/%Y/%m/%d/', - root='/data') + write_path_template='/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/merlin/%Y/%m/%d', + root='/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/merlin',) proc1 = Cpt(ProcessPlugin, 'Proc1:') stats1 = Cpt(StatsPlugin, 'Stats1:') From fd4940c0a5f90d48faebc10dbda71d3f56c4b9b3 Mon Sep 17 00:00:00 2001 From: jennmald Date: Thu, 26 Jun 2025 15:09:23 -0400 Subject: [PATCH 07/32] fix dtype --- src/cditools/merlin.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index bab48113..19e748a5 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -29,6 +29,15 @@ def get_frames_per_point(self): return 1 else: return self.parent.cam.num_images.get() + + def describe(self): + ret = super().describe() + key = self.parent._image_name + cam_dtype = self.parent.cam.data_type.get(as_string=True) + type_map = {'UInt8': '|u1', 'UInt16': ' Date: Thu, 26 Jun 2025 15:14:36 -0400 Subject: [PATCH 08/32] fix hdf5 warning --- src/cditools/merlin.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 19e748a5..4f18b0a4 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -80,6 +80,15 @@ def stage(self): logger.info("Staged") return staged + def describe(self): + ret = super().describe() + key = self.parent._image_name + cam_dtype = self.parent.cam.data_type.get(as_string=True) + type_map = {'UInt8': '|u1', 'UInt16': ' Date: Thu, 26 Jun 2025 15:21:03 -0400 Subject: [PATCH 09/32] hdf5 with file store --- src/cditools/merlin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 4f18b0a4..aa3ccce0 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -106,6 +106,17 @@ def stage(self): # ensure that setting capture is the last thing that's done self.stage_sigs.move_to_end(self.capture) return super().stage() + + def describe(self): + ret = super().describe() + key = self.parent._image_name + cam_dtype = self.parent.cam.data_type.get(as_string=True) + type_map = {'UInt8': '|u1', 'UInt16': ' Date: Fri, 27 Jun 2025 08:27:34 -0400 Subject: [PATCH 10/32] debugging --- src/cditools/merlin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index aa3ccce0..764b0805 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -34,6 +34,7 @@ def describe(self): ret = super().describe() key = self.parent._image_name cam_dtype = self.parent.cam.data_type.get(as_string=True) + print(cam_dtype) type_map = {'UInt8': '|u1', 'UInt16': ' Date: Fri, 27 Jun 2025 08:48:07 -0400 Subject: [PATCH 11/32] fix data type --- src/cditools/merlin.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 764b0805..f5d4b0d5 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -1,6 +1,5 @@ from __future__ import print_function import logging -from pathlib import PurePath from ophyd import (AreaDetector, CamBase, TIFFPlugin, Component as Cpt, HDF5Plugin, Device, StatsPlugin, ProcessPlugin, ROIPlugin, TransformPlugin, EpicsSignal) @@ -34,10 +33,7 @@ def describe(self): ret = super().describe() key = self.parent._image_name cam_dtype = self.parent.cam.data_type.get(as_string=True) - print(cam_dtype) - type_map = {'UInt8': '|u1', 'UInt16': ' Date: Fri, 27 Jun 2025 09:04:02 -0400 Subject: [PATCH 12/32] clean up data types --- src/cditools/merlin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index f5d4b0d5..9ce74ced 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -32,7 +32,6 @@ def get_frames_per_point(self): def describe(self): ret = super().describe() key = self.parent._image_name - cam_dtype = self.parent.cam.data_type.get(as_string=True) ret[key].setdefault('dtype_str', ' Date: Wed, 9 Jul 2025 13:26:28 -0400 Subject: [PATCH 13/32] fix precommit --- src/cditools/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cditools/utils.py b/src/cditools/utils.py index 9126d4a6..6b137fbe 100644 --- a/src/cditools/utils.py +++ b/src/cditools/utils.py @@ -30,4 +30,4 @@ def ordered_dict_move_to_beginning(od, key): if k != key) od.clear() od[key] = value - od.update(items) \ No newline at end of file + od.update(items) From 1b99844426343728f99bad0e677380d7ac6903d6 Mon Sep 17 00:00:00 2001 From: thomashopkins32 Date: Tue, 27 May 2025 16:28:53 -0400 Subject: [PATCH 14/32] Check for mutliple master files for each datum --- src/cditools/eiger.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/cditools/eiger.py b/src/cditools/eiger.py index 33ec826e..3e91bf4a 100644 --- a/src/cditools/eiger.py +++ b/src/cditools/eiger.py @@ -1,4 +1,11 @@ +<<<<<<< HEAD from __future__ import annotations +======= +import os +from datetime import datetime +from pathlib import PurePath +from typing import Any, Optional +>>>>>>> c849837 (Check for mutliple master files for each datum) from datetime import datetime from pathlib import Path, PurePath @@ -58,6 +65,16 @@ def __init__(self, *args: Any, **kwargs: dict[str, Any]) -> None: self.filestore_spec = "AD_EIGER" self._master_file_paths: list[PurePath] = [] + @property + def master_file_paths(self) -> list[PurePath]: + if len(self._master_file_paths) == 0: + raise ValueError("Master file path has not been set. Call stage() first.") + return self._master_file_paths + + @property + def sequence_number(self) -> int: + return self.sequence_id_offset + self.sequence_id.get() + @property def master_file_paths(self) -> list[PurePath]: if len(self._master_file_paths) == 0: @@ -71,9 +88,15 @@ def sequence_number(self) -> int: def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverride] res_uid = new_short_uid() +<<<<<<< HEAD write_path = Path(f"{datetime.now().strftime(self.write_path_template)}/") self.file_path.set(write_path.as_posix()).wait(1.0) +======= + write_path = f"{datetime.now().strftime(self.write_path_template)}/" + self.file_path.set(write_path).wait(1.0) + +>>>>>>> c849837 (Check for mutliple master files for each datum) # The name pattern must have `$id` in it. # `$id` is replaced by the current sequence id of the acquisition. # E.g. * _1_master.h5 @@ -94,6 +117,7 @@ def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverrid self._generate_resource(resource_kwargs) # Validate that the root path exists +<<<<<<< HEAD if not Path.exists(Path(self.reg_root)): msg = f"Root path {self.reg_root} does not exist" raise FileNotFoundError(msg) @@ -117,11 +141,29 @@ def generate_datum( self._master_file_paths.append( PurePath(f"{self._fn}_{self.sequence_number}_master.h5") ) +======= + if not os.path.exists(self.reg_root): + raise FileNotFoundError(f"Root path {self.reg_root} does not exist") + + # Create the templated part of the path + if not os.path.exists(write_path): + os.makedirs(write_path) + + self._master_file_paths = [] + + def generate_datum(self, key: str, timestamp: float, datum_kwargs: dict[str, Any]) -> Any: + # The detector keeps its own counter which is uses label HDF5 + # sub-files. We access that counter via the sequence_id + # signal and stash it in the datum. + datum_kwargs.update({'seq_id': self.sequence_number}) + self._master_file_paths.append(f"{self._fn}_{self.sequence_number}_master.h5") +>>>>>>> c849837 (Check for mutliple master files for each datum) return super().generate_datum(key, timestamp, datum_kwargs) class EigerBase(EigerDetector): """Base class for Eiger detectors that have the commonly used plugins.""" +<<<<<<< HEAD file_handler = Cpt( EigerFileHandler, @@ -131,6 +173,11 @@ class EigerBase(EigerDetector): write_path_template="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger/%Y/%m/%d", root="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger", ) +======= + file_handler = Cpt(EigerFileHandler, "cam1:", name="file_handler", + write_path_template="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger/%Y/%m/%d", + root="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger") +>>>>>>> c849837 (Check for mutliple master files for each datum) stats1 = Cpt(StatsPlugin, "Stats1:") stats2 = Cpt(StatsPlugin, "Stats2:") stats3 = Cpt(StatsPlugin, "Stats3:") @@ -143,6 +190,7 @@ class EigerBase(EigerDetector): proc1 = Cpt(ProcessPlugin, "Proc1:") def stage(self, *args: Any, **kwargs: dict[str, Any]) -> list[object]: +<<<<<<< HEAD staged_devices: list[object] = super().stage(*args, **kwargs) self.cam.manual_trigger.set(True).wait(5.0) file_write_path: Path = Path(cast(str, self.file_handler.file_path.get())) @@ -159,6 +207,21 @@ def unstage(self) -> list[object]: msg = f"Paths {self.file_handler.master_file_paths} were not written." raise FileNotFoundError(msg) return ret +======= + staged_devices = super().stage(*args, **kwargs) + self.cam.manual_trigger.set(True).wait(5.0) + file_write_path = self.file_handler.file_path.get() + if not os.path.exists(file_write_path): + raise FileNotFoundError(f"Path {file_write_path} does not exist.") + return staged_devices + + def unstage(self) -> None: + self.cam.manual_trigger.set(False).wait(5.0) + super().unstage() +>>>>>>> c849837 (Check for mutliple master files for each datum) + + if not all(os.path.exists(path) for path in self.file_handler.master_file_paths): + raise FileNotFoundError(f"Paths {self.file_handler.master_file_paths} were not written.") class EigerSingleTrigger(SingleTrigger, EigerBase): # type: ignore[reportIncompatibleMethodOverride] @@ -171,7 +234,11 @@ def __init__(self, *args: Any, **kwargs: dict[str, Any]) -> None: self.stage_sigs["file_handler.enable"] = True self.stage_sigs["file_handler.save_files"] = True +<<<<<<< HEAD def trigger(self, *args: Any, **kwargs: dict[str, Any]) -> ADTriggerStatus: +======= + def trigger(self, *args: Any, **kwargs: dict[str, Any]) -> StatusBase: +>>>>>>> c849837 (Check for mutliple master files for each datum) status = super().trigger(*args, **kwargs) # If the manual trigger is enabled, we need to press the special trigger button # to actually trigger the detector. From e9bd7f4493a24a32d7bec509a8bdec0556e04fbb Mon Sep 17 00:00:00 2001 From: thomashopkins32 Date: Tue, 27 May 2025 16:47:36 -0400 Subject: [PATCH 15/32] pre-commit --- src/cditools/eiger.py | 68 ++++--------------------------------------- 1 file changed, 6 insertions(+), 62 deletions(-) diff --git a/src/cditools/eiger.py b/src/cditools/eiger.py index 3e91bf4a..75564090 100644 --- a/src/cditools/eiger.py +++ b/src/cditools/eiger.py @@ -1,11 +1,4 @@ -<<<<<<< HEAD from __future__ import annotations -======= -import os -from datetime import datetime -from pathlib import PurePath -from typing import Any, Optional ->>>>>>> c849837 (Check for mutliple master files for each datum) from datetime import datetime from pathlib import Path, PurePath @@ -68,12 +61,13 @@ def __init__(self, *args: Any, **kwargs: dict[str, Any]) -> None: @property def master_file_paths(self) -> list[PurePath]: if len(self._master_file_paths) == 0: - raise ValueError("Master file path has not been set. Call stage() first.") + msg = "Master file path has not been set. Call stage() first." + raise ValueError(msg) return self._master_file_paths @property def sequence_number(self) -> int: - return self.sequence_id_offset + self.sequence_id.get() + return self.sequence_id_offset + int(self.sequence_id.get()) @property def master_file_paths(self) -> list[PurePath]: @@ -88,15 +82,9 @@ def sequence_number(self) -> int: def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverride] res_uid = new_short_uid() -<<<<<<< HEAD write_path = Path(f"{datetime.now().strftime(self.write_path_template)}/") self.file_path.set(write_path.as_posix()).wait(1.0) -======= - write_path = f"{datetime.now().strftime(self.write_path_template)}/" - self.file_path.set(write_path).wait(1.0) - ->>>>>>> c849837 (Check for mutliple master files for each datum) # The name pattern must have `$id` in it. # `$id` is replaced by the current sequence id of the acquisition. # E.g. * _1_master.h5 @@ -111,13 +99,12 @@ def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverrid file_prefix = PurePath(self.file_path.get()) / res_uid self._fn = file_prefix - images_per_file: str = self.file_write_images_per_file.get() - resource_kwargs: dict[str, str] = {"images_per_file": images_per_file} + images_per_file = self.file_write_images_per_file.get() + resource_kwargs = {"images_per_file": images_per_file} self._generate_resource(resource_kwargs) # Validate that the root path exists -<<<<<<< HEAD if not Path.exists(Path(self.reg_root)): msg = f"Root path {self.reg_root} does not exist" raise FileNotFoundError(msg) @@ -141,29 +128,11 @@ def generate_datum( self._master_file_paths.append( PurePath(f"{self._fn}_{self.sequence_number}_master.h5") ) -======= - if not os.path.exists(self.reg_root): - raise FileNotFoundError(f"Root path {self.reg_root} does not exist") - - # Create the templated part of the path - if not os.path.exists(write_path): - os.makedirs(write_path) - - self._master_file_paths = [] - - def generate_datum(self, key: str, timestamp: float, datum_kwargs: dict[str, Any]) -> Any: - # The detector keeps its own counter which is uses label HDF5 - # sub-files. We access that counter via the sequence_id - # signal and stash it in the datum. - datum_kwargs.update({'seq_id': self.sequence_number}) - self._master_file_paths.append(f"{self._fn}_{self.sequence_number}_master.h5") ->>>>>>> c849837 (Check for mutliple master files for each datum) return super().generate_datum(key, timestamp, datum_kwargs) class EigerBase(EigerDetector): """Base class for Eiger detectors that have the commonly used plugins.""" -<<<<<<< HEAD file_handler = Cpt( EigerFileHandler, @@ -173,11 +142,6 @@ class EigerBase(EigerDetector): write_path_template="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger/%Y/%m/%d", root="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger", ) -======= - file_handler = Cpt(EigerFileHandler, "cam1:", name="file_handler", - write_path_template="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger/%Y/%m/%d", - root="/nsls2/data/tst/legacy/mock-proposals/2025-2/pass-56789/assets/eiger") ->>>>>>> c849837 (Check for mutliple master files for each datum) stats1 = Cpt(StatsPlugin, "Stats1:") stats2 = Cpt(StatsPlugin, "Stats2:") stats3 = Cpt(StatsPlugin, "Stats3:") @@ -190,7 +154,6 @@ class EigerBase(EigerDetector): proc1 = Cpt(ProcessPlugin, "Proc1:") def stage(self, *args: Any, **kwargs: dict[str, Any]) -> list[object]: -<<<<<<< HEAD staged_devices: list[object] = super().stage(*args, **kwargs) self.cam.manual_trigger.set(True).wait(5.0) file_write_path: Path = Path(cast(str, self.file_handler.file_path.get())) @@ -207,22 +170,7 @@ def unstage(self) -> list[object]: msg = f"Paths {self.file_handler.master_file_paths} were not written." raise FileNotFoundError(msg) return ret -======= - staged_devices = super().stage(*args, **kwargs) - self.cam.manual_trigger.set(True).wait(5.0) - file_write_path = self.file_handler.file_path.get() - if not os.path.exists(file_write_path): - raise FileNotFoundError(f"Path {file_write_path} does not exist.") - return staged_devices - - def unstage(self) -> None: - self.cam.manual_trigger.set(False).wait(5.0) - super().unstage() ->>>>>>> c849837 (Check for mutliple master files for each datum) - - if not all(os.path.exists(path) for path in self.file_handler.master_file_paths): - raise FileNotFoundError(f"Paths {self.file_handler.master_file_paths} were not written.") - + class EigerSingleTrigger(SingleTrigger, EigerBase): # type: ignore[reportIncompatibleMethodOverride] """Eiger detector that uses the single trigger acquisition mode.""" @@ -234,11 +182,7 @@ def __init__(self, *args: Any, **kwargs: dict[str, Any]) -> None: self.stage_sigs["file_handler.enable"] = True self.stage_sigs["file_handler.save_files"] = True -<<<<<<< HEAD def trigger(self, *args: Any, **kwargs: dict[str, Any]) -> ADTriggerStatus: -======= - def trigger(self, *args: Any, **kwargs: dict[str, Any]) -> StatusBase: ->>>>>>> c849837 (Check for mutliple master files for each datum) status = super().trigger(*args, **kwargs) # If the manual trigger is enabled, we need to press the special trigger button # to actually trigger the detector. From 916a1dfa52f557bce049b7dec6a1fc69ab52c01a Mon Sep 17 00:00:00 2001 From: jennmald Date: Thu, 10 Jul 2025 11:50:13 -0400 Subject: [PATCH 16/32] fix ruff --- src/cditools/merlin.py | 147 +++++++++++++++++++-------------- src/cditools/trigger_mixins.py | 96 +++++++++++---------- src/cditools/utils.py | 18 ++-- 3 files changed, 146 insertions(+), 115 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 9ce74ced..f279ce7f 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -1,74 +1,89 @@ -from __future__ import print_function +from __future__ import annotations + import logging -from ophyd import (AreaDetector, CamBase, TIFFPlugin, Component as Cpt, - HDF5Plugin, Device, StatsPlugin, ProcessPlugin, - ROIPlugin, TransformPlugin, EpicsSignal) -from ophyd.areadetector import EpicsSignalWithRBV +from ophyd import ( + AreaDetector, + CamBase, + Device, + EpicsSignal, + HDF5Plugin, + ProcessPlugin, + ROIPlugin, + StatsPlugin, + TIFFPlugin, + TransformPlugin, +) +from ophyd import Component as Cpt +from ophyd.areadetector import EpicsSignalWithRBV from ophyd.areadetector.base import ADComponent -from ophyd.areadetector.filestore_mixins import ( - FileStoreTIFF, FileStorePluginBase) +from ophyd.areadetector.filestore_mixins import FileStorePluginBase, FileStoreTIFF +from .trigger_mixins import CDIModalTrigger, FileStoreBulkReadable from .utils import makedirs -from .trigger_mixins import (CDIModalTrigger, FileStoreBulkReadable) - logger = logging.getLogger(__name__) -class MerlinTiffPlugin(TIFFPlugin, FileStoreBulkReadable, FileStoreTIFF, - Device): +class MerlinTiffPlugin(TIFFPlugin, FileStoreBulkReadable, FileStoreTIFF, Device): def mode_external(self): total_points = self.parent.mode_settings.total_points.get() self.stage_sigs[self.num_capture] = total_points def get_frames_per_point(self): mode = self.parent.mode_settings.mode.get() - if mode == 'external': + if mode == "external": return 1 - else: - return self.parent.cam.num_images.get() - + return self.parent.cam.num_images.get() + def describe(self): ret = super().describe() key = self.parent._image_name - ret[key].setdefault('dtype_str', ' Date: Fri, 11 Jul 2025 12:23:30 -0400 Subject: [PATCH 17/32] more fixes for mypy --- src/cditools/merlin.py | 29 ++++++++++++++++---------- src/cditools/trigger_mixins.py | 37 ++++++++++++++++++---------------- src/cditools/utils.py | 5 +++-- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index f279ce7f..f477f818 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -26,17 +26,17 @@ class MerlinTiffPlugin(TIFFPlugin, FileStoreBulkReadable, FileStoreTIFF, Device): - def mode_external(self): + def mode_external(self) -> None: total_points = self.parent.mode_settings.total_points.get() self.stage_sigs[self.num_capture] = total_points - def get_frames_per_point(self): + def get_frames_per_point(self) -> object: mode = self.parent.mode_settings.mode.get() if mode == "external": return 1 return self.parent.cam.num_images.get() - def describe(self): + def describe(self) -> object: ret = super().describe() key = self.parent._image_name ret[key].setdefault("dtype_str", " None: super().__init__(*args, **kwargs) self.stage_sigs.update( [ @@ -77,7 +77,7 @@ def __init__(self, *args, **kwargs): ] ) - def stage(self): + def stage(self) -> object: logger.info("Staging") staged = super().stage() logger.info("Staging step 2") @@ -91,13 +91,13 @@ def stage(self): logger.info("Staged") return staged - def describe(self): + def describe(self) -> dict[str, dict]: ret = super().describe() key = self.parent._image_name ret[key].setdefault("dtype_str", " tuple[str, str, str]: fn, read_path, write_path = super().make_filename() mode_settings = self.parent.mode_settings if mode_settings.make_directories.get(): @@ -106,7 +106,7 @@ def make_filename(self): class HDF5PluginWithFileStore(HDF5Plugin, MerlinFileStoreHDF5): - def stage(self): + def stage(self) -> object: mode_settings = self.parent.mode_settings total_points = mode_settings.total_points.get() self.stage_sigs[self.num_capture] = total_points @@ -144,7 +144,14 @@ class CDIMerlinDetector(CDIModalTrigger, MerlinDetector): roi3 = Cpt(ROIPlugin, "ROI3:") roi4 = Cpt(ROIPlugin, "ROI4:") - def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): + def __init__( + self, + prefix, + *, + read_attrs: list[str] = None, + configuration_attrs: list[str] = None, + **kwargs, + ): if read_attrs is None: read_attrs = ["hdf5", "cam"] if configuration_attrs is None: @@ -161,7 +168,7 @@ def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwarg **kwargs, ) - def mode_internal(self): + def mode_internal(self) -> None: super().mode_internal() count_time = self.count_time.get() @@ -169,7 +176,7 @@ def mode_internal(self): self.stage_sigs[self.cam.acquire_time] = count_time self.stage_sigs[self.cam.acquire_period] = count_time + 0.005 - def mode_external(self): + def mode_external(self) -> None: super().mode_external() # NOTE: these values specify a debounce time for external triggering so diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 2ea5669e..66bd824b 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -3,6 +3,7 @@ import itertools import logging import time as ttime +from typing import Optional from ophyd import ( Signal, @@ -17,7 +18,7 @@ class TriggerBase(BlueskyInterface): - def __init__(self, *args, **kwargs): + def __init__(self, *args: object, **kwargs: object) -> None: super().__init__(*args, **kwargs) # If acquiring, stop. @@ -44,7 +45,7 @@ class CDIModalBase(Device): Signal, value=1.0, doc="Exposure/count time, as specified by bluesky" ) - def mode_setup(self, mode): + def mode_setup(self, mode: str) -> None: devices = [self] + [getattr(self, attr) for attr in self._sub_devices] attr = f"mode_{mode}" for dev in devices: @@ -52,24 +53,24 @@ def mode_setup(self, mode): mode_setup_method = getattr(dev, attr) mode_setup_method() - def mode_internal(self): + def mode_internal(self) -> None: logger.debug("%s internal triggering %s", self.name, self.mode_settings.get()) - def mode_external(self): + def mode_external(self) -> None: logger.debug("%s external triggering %s", self.name, self.mode_settings.get()) @property - def mode(self): + def mode(self) -> str: """Trigger mode (external/internal)""" return self.mode_settings.mode.get() - def stage(self): + def stage(self) -> object: if self._staged != Staged.yes: self.mode_setup(self.mode) return super().stage() - def unstage(self): + def unstage(self) -> None: if self.mode == "external": logger.info( "[Unstage] Stopping externally-triggered detector %s", self.name @@ -80,19 +81,21 @@ def unstage(self): class CDIModalTrigger(CDIModalBase, TriggerBase): - def __init__(self, *args, image_name=None, **kwargs): + def __init__( + self, *args: object, image_name: Optional[str] = None, **kwargs: object + ): super().__init__(*args, **kwargs) if image_name is None: image_name = "_".join([self.name, "image"]) self._image_name = image_name self._external_acquire_at_stage = True - def stop(self, success=False): + def stop(self, success=False) -> None: ret = super().stop(success=success) self._acquisition_signal.put(0, wait=True) return ret - def mode_internal(self): + def mode_internal(self) -> None: super().mode_internal() cam = self.cam @@ -103,7 +106,7 @@ def mode_internal(self): cam.stage_sigs[cam.image_mode] = "Single" cam.stage_sigs[cam.trigger_mode] = "Internal" - def mode_external(self): + def mode_external(self) -> None: super().mode_external() total_points = self.mode_settings.total_points.get() @@ -112,7 +115,7 @@ def mode_external(self): cam.stage_sigs[cam.image_mode] = "Multiple" cam.stage_sigs[cam.trigger_mode] = "External" - def stage(self): + def stage(self) -> object: self._acquisition_signal.subscribe(self._acquire_changed) staged = super().stage() @@ -122,13 +125,13 @@ def stage(self): self._acquisition_signal.put(1, wait=False) return staged - def unstage(self): + def unstage(self) -> object: try: return super().unstage() finally: self._acquisition_signal.clear_sub(self._acquire_changed) - def trigger_internal(self): + def trigger_internal(self) -> DeviceStatus: if self._staged != Staged.yes: msg = ( "This detector is not ready to trigger." @@ -141,7 +144,7 @@ def trigger_internal(self): self.dispatch(self._image_name, ttime.time()) return self._status - def trigger_external(self): + def trigger_external(self) -> DeviceStatus: if self._staged != Staged.yes: msg = ( "This detector is not ready to trigger." @@ -161,7 +164,7 @@ def trigger(self): mode_trigger = getattr(self, f"trigger_{self.mode}") return mode_trigger() - def _acquire_changed(self, value=None, old_value=None): + def _acquire_changed(self, value=None, old_value=None) -> None: """This is called when the 'acquire' signal changes.""" if self._status is None: return @@ -171,7 +174,7 @@ def _acquire_changed(self, value=None, old_value=None): class FileStoreBulkReadable(FileStoreIterativeWrite): - def _reset_data(self): + def _reset_data(self) -> None: self._datum_uids.clear() self._point_counter = itertools.count() diff --git a/src/cditools/utils.py b/src/cditools/utils.py index cfb0dda5..08952a63 100644 --- a/src/cditools/utils.py +++ b/src/cditools/utils.py @@ -2,9 +2,10 @@ import os from pathlib import Path +from typing import Any -def makedirs(path, mode=0o777): +def makedirs(path: str, mode: int = 0o777) -> list[str]: """Recursively make directories and set permissions""" # Permissions not working with os.makedirs - # See: http://stackoverflow.com/questions/5231901 @@ -24,7 +25,7 @@ def makedirs(path, mode=0o777): return ret -def ordered_dict_move_to_beginning(od, key): +def ordered_dict_move_to_beginning(od: dict[str, Any], key: str) -> None: if key not in od: return From f9786926f57378d60c30b7ce1ee832f3943c8b3f Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 11 Jul 2025 12:59:01 -0400 Subject: [PATCH 18/32] ignore ophyd errors --- mypy.ini | 2 ++ src/cditools/trigger_mixins.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..1215375e --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = True \ No newline at end of file diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 66bd824b..6ba96287 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -3,7 +3,7 @@ import itertools import logging import time as ttime -from typing import Optional +from typing import Any, Optional from ophyd import ( Signal, @@ -70,7 +70,7 @@ def stage(self) -> object: return super().stage() - def unstage(self) -> None: + def unstage(self) -> Any: if self.mode == "external": logger.info( "[Unstage] Stopping externally-triggered detector %s", self.name From 887a4a1fbbec4c68b485ce022b3e427a20d7253f Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 11 Jul 2025 13:09:31 -0400 Subject: [PATCH 19/32] add new line: --- mypy.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index 1215375e..976ba029 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,2 +1,2 @@ [mypy] -ignore_missing_imports = True \ No newline at end of file +ignore_missing_imports = True From df2a8c15e17ea8e2b685357ba5c6e9522cf3dcfb Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 11 Jul 2025 13:09:52 -0400 Subject: [PATCH 20/32] finished trigger mixins --- src/cditools/trigger_mixins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 6ba96287..f2963a78 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -153,7 +153,8 @@ def trigger_external(self) -> DeviceStatus: raise RuntimeError(msg) self._status = DeviceStatus(self) - self._status._finished() + if self._status is not None: + self._status._finished() # TODO this timestamp is inaccurate! if self.mode_settings.scan_type.get() != "fly": # Don't dispatch images for fly-scans - they are bulk read at the end From bca24de1bb58a47dc61deb1bf36fb54e00df6f2b Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 11 Jul 2025 13:13:04 -0400 Subject: [PATCH 21/32] passing mypy: --- src/cditools/merlin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index f477f818..ad1c088d 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -1,6 +1,7 @@ from __future__ import annotations import logging +from typing import Optional from ophyd import ( AreaDetector, @@ -148,8 +149,8 @@ def __init__( self, prefix, *, - read_attrs: list[str] = None, - configuration_attrs: list[str] = None, + read_attrs: Optional[list[str]] = None, + configuration_attrs: Optional[list[str]] = None, **kwargs, ): if read_attrs is None: From 6c78c4172443d7c2cca03dd383f95a9e04991168 Mon Sep 17 00:00:00 2001 From: jennmald Date: Fri, 11 Jul 2025 13:19:39 -0400 Subject: [PATCH 22/32] satisfy ruff --- src/cditools/merlin.py | 5 ++--- src/cditools/trigger_mixins.py | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index ad1c088d..7c156c50 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -from typing import Optional from ophyd import ( AreaDetector, @@ -149,8 +148,8 @@ def __init__( self, prefix, *, - read_attrs: Optional[list[str]] = None, - configuration_attrs: Optional[list[str]] = None, + read_attrs: list[str] | None = None, + configuration_attrs: list[str] | None = None, **kwargs, ): if read_attrs is None: diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index f2963a78..9d0eed9c 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -3,7 +3,7 @@ import itertools import logging import time as ttime -from typing import Any, Optional +from typing import Any from ophyd import ( Signal, @@ -81,9 +81,7 @@ def unstage(self) -> Any: class CDIModalTrigger(CDIModalBase, TriggerBase): - def __init__( - self, *args: object, image_name: Optional[str] = None, **kwargs: object - ): + def __init__(self, *args: object, image_name: str | None = None, **kwargs: object): super().__init__(*args, **kwargs) if image_name is None: image_name = "_".join([self.name, "image"]) From 2a5eaf68d4eaa70a60d31f06518ec6ac8a56ab16 Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 10:32:17 -0400 Subject: [PATCH 23/32] blank space --- src/cditools/eiger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cditools/eiger.py b/src/cditools/eiger.py index 75564090..3f858198 100644 --- a/src/cditools/eiger.py +++ b/src/cditools/eiger.py @@ -170,7 +170,7 @@ def unstage(self) -> list[object]: msg = f"Paths {self.file_handler.master_file_paths} were not written." raise FileNotFoundError(msg) return ret - + class EigerSingleTrigger(SingleTrigger, EigerBase): # type: ignore[reportIncompatibleMethodOverride] """Eiger detector that uses the single trigger acquisition mode.""" From ffefd6ff7e170eae2a50374fcd16745bea5655ee Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 10:54:40 -0400 Subject: [PATCH 24/32] fix eiger merge conflicts --- src/cditools/eiger.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/cditools/eiger.py b/src/cditools/eiger.py index 3f858198..acda7932 100644 --- a/src/cditools/eiger.py +++ b/src/cditools/eiger.py @@ -69,17 +69,6 @@ def master_file_paths(self) -> list[PurePath]: def sequence_number(self) -> int: return self.sequence_id_offset + int(self.sequence_id.get()) - @property - def master_file_paths(self) -> list[PurePath]: - if len(self._master_file_paths) == 0: - msg = "Master file path has not been set. Call stage() first." - raise ValueError(msg) - return self._master_file_paths - - @property - def sequence_number(self) -> int: - return self.sequence_id_offset + int(self.sequence_id.get()) - def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverride] res_uid = new_short_uid() write_path = Path(f"{datetime.now().strftime(self.write_path_template)}/") From 7d57e6bc8f1ab4e7174b6c7303b7e4a7589790c7 Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 10:57:29 -0400 Subject: [PATCH 25/32] fix utils --- src/cditools/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cditools/utils.py b/src/cditools/utils.py index 08952a63..6af472c8 100644 --- a/src/cditools/utils.py +++ b/src/cditools/utils.py @@ -12,7 +12,7 @@ def makedirs(path: str, mode: int = 0o777) -> list[str]: if not path or Path(path).exists(): return [] - head, tail = os.path.split(path) + head, _ = os.path.split(path) ret = makedirs(head, mode) try: Path(path).mkdir() From c40433e9a02f4c2c2f9830a5d99e4f42943095bb Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 14:17:26 -0400 Subject: [PATCH 26/32] fix pyright --- mypy.ini | 2 -- pyrightconfig.json | 5 +++++ src/cditools/merlin.py | 37 +++++++++++++++++----------------- src/cditools/trigger_mixins.py | 25 ++++++++++++++--------- 4 files changed, 40 insertions(+), 29 deletions(-) delete mode 100644 mypy.ini create mode 100644 pyrightconfig.json diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 976ba029..00000000 --- a/mypy.ini +++ /dev/null @@ -1,2 +0,0 @@ -[mypy] -ignore_missing_imports = True diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 00000000..f92b3d6b --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,5 @@ +{ + "typeCheckingMode": "basic", + "reportMissingTypeStubs": false, + "reportUntypedBaseClass": false +} \ No newline at end of file diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 7c156c50..8ca1734b 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -1,6 +1,7 @@ from __future__ import annotations import logging +from collections import OrderedDict from ophyd import ( AreaDetector, @@ -27,19 +28,19 @@ class MerlinTiffPlugin(TIFFPlugin, FileStoreBulkReadable, FileStoreTIFF, Device): def mode_external(self) -> None: - total_points = self.parent.mode_settings.total_points.get() + total_points = self.parent.mode_settings.total_points.get() # type: ignore[union-attr] self.stage_sigs[self.num_capture] = total_points def get_frames_per_point(self) -> object: - mode = self.parent.mode_settings.mode.get() + mode = self.parent.mode_settings.mode.get() # type: ignore[union-attr] if mode == "external": return 1 - return self.parent.cam.num_images.get() + return self.parent.cam.num_images.get() # type: ignore[union-attr] def describe(self) -> object: ret = super().describe() - key = self.parent._image_name - ret[key].setdefault("dtype_str", " None: super().__init__(*args, **kwargs) self.stage_sigs.update( [ - (self.file_template, "%s%s_%6.6d.h5"), - (self.file_write_mode, "Stream"), - (self.compression, "zlib"), - (self.capture, 1), + (self.file_template, "%s%s_%6.6d.h5"), # type: ignore[attr-defined] + (self.file_write_mode, "Stream"), # type: ignore[attr-defined] + (self.compression, "zlib"), # type: ignore[attr-defined] + (self.capture, 1), # type: ignore[attr-defined] ] ) @@ -91,15 +92,15 @@ def stage(self) -> object: logger.info("Staged") return staged - def describe(self) -> dict[str, dict]: + def describe(self) -> OrderedDict[str, dict]: ret = super().describe() - key = self.parent._image_name - ret[key].setdefault("dtype_str", " tuple[str, str, str]: fn, read_path, write_path = super().make_filename() - mode_settings = self.parent.mode_settings + mode_settings = self.parent.mode_settings # type: ignore[union-attr] if mode_settings.make_directories.get(): makedirs(read_path) return fn, read_path, write_path @@ -107,7 +108,7 @@ def make_filename(self) -> tuple[str, str, str]: class HDF5PluginWithFileStore(HDF5Plugin, MerlinFileStoreHDF5): def stage(self) -> object: - mode_settings = self.parent.mode_settings + mode_settings = self.parent.mode_settings # type: ignore[union-attr] total_points = mode_settings.total_points.get() self.stage_sigs[self.num_capture] = total_points @@ -117,8 +118,8 @@ def stage(self) -> object: def describe(self): ret = super().describe() - key = self.parent._image_name - ret[key].setdefault("dtype_str", " None: super().mode_internal() count_time = self.count_time.get() - if count_time is not None: + if isinstance(count_time, float): self.stage_sigs[self.cam.acquire_time] = count_time self.stage_sigs[self.cam.acquire_period] = count_time + 0.005 diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 9d0eed9c..b9fbaab7 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -3,6 +3,7 @@ import itertools import logging import time as ttime +from collections.abc import Sequence from typing import Any from ophyd import ( @@ -22,9 +23,9 @@ def __init__(self, *args: object, **kwargs: object) -> None: super().__init__(*args, **kwargs) # If acquiring, stop. - self.stage_sigs[self.cam.acquire] = 0 - self.stage_sigs[self.cam.image_mode] = "Multiple" - self._acquisition_signal = self.cam.acquire + self.stage_sigs[self.cam.acquire] = 0 # type: ignore[attr-defined] + self.stage_sigs[self.cam.image_mode] = "Multiple" # type: ignore[attr-defined] + self._acquisition_signal = self.cam.acquire # type: ignore[attr-defined] self._status = None @@ -60,7 +61,7 @@ def mode_external(self) -> None: logger.debug("%s external triggering %s", self.name, self.mode_settings.get()) @property - def mode(self) -> str: + def mode(self) -> Any: """Trigger mode (external/internal)""" return self.mode_settings.mode.get() @@ -81,8 +82,14 @@ def unstage(self) -> Any: class CDIModalTrigger(CDIModalBase, TriggerBase): - def __init__(self, *args: object, image_name: str | None = None, **kwargs: object): - super().__init__(*args, **kwargs) + def __init__( + self, + prefix: str, + *args: object, + image_name: str | None = None, + **kwargs: object, + ) -> None: + super().__init__(prefix, *args, **kwargs) if image_name is None: image_name = "_".join([self.name, "image"]) self._image_name = image_name @@ -177,7 +184,7 @@ def _reset_data(self) -> None: self._datum_uids.clear() self._point_counter = itertools.count() - def bulk_read(self, timestamps): + def bulk_read(self, timestamps: Sequence[float]) -> dict[str, list[str]]: image_name = self.image_name uids = [self.generate_datum(self.image_name, ts, {}) for ts in timestamps] @@ -187,5 +194,5 @@ def bulk_read(self, timestamps): return {image_name: uids} @property - def image_name(self): - return self.parent._image_name + def image_name(self) -> str: + return self.parent._image_name # type: ignore[attr-defined] From b4f097fba915d6798812fd29f01def2479ceca76 Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 14:21:59 -0400 Subject: [PATCH 27/32] eof --- menv/bin/Activate.ps1 | 247 +++++++++++++++++++++++++++++ menv/bin/activate | 70 ++++++++ menv/bin/activate.csh | 27 ++++ menv/bin/activate.fish | 69 ++++++++ menv/bin/f2py | 8 + menv/bin/nodeenv | 8 + menv/bin/numpy-config | 8 + menv/bin/pint-convert | 8 + menv/bin/pip | 8 + menv/bin/pip3 | 8 + menv/bin/pip3.12 | 8 + menv/bin/py.test | 8 + menv/bin/pygmentize | 8 + menv/bin/pyright | 8 + menv/bin/pyright-langserver | 8 + menv/bin/pyright-python | 8 + menv/bin/pyright-python-langserver | 8 + menv/bin/pytest | 8 + menv/bin/python | 1 + menv/bin/python3 | 1 + menv/bin/python3.12 | 1 + menv/pyvenv.cfg | 5 + pyrightconfig.json | 2 +- 23 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 menv/bin/Activate.ps1 create mode 100644 menv/bin/activate create mode 100644 menv/bin/activate.csh create mode 100644 menv/bin/activate.fish create mode 100755 menv/bin/f2py create mode 100755 menv/bin/nodeenv create mode 100755 menv/bin/numpy-config create mode 100755 menv/bin/pint-convert create mode 100755 menv/bin/pip create mode 100755 menv/bin/pip3 create mode 100755 menv/bin/pip3.12 create mode 100755 menv/bin/py.test create mode 100755 menv/bin/pygmentize create mode 100755 menv/bin/pyright create mode 100755 menv/bin/pyright-langserver create mode 100755 menv/bin/pyright-python create mode 100755 menv/bin/pyright-python-langserver create mode 100755 menv/bin/pytest create mode 120000 menv/bin/python create mode 120000 menv/bin/python3 create mode 120000 menv/bin/python3.12 create mode 100644 menv/pyvenv.cfg diff --git a/menv/bin/Activate.ps1 b/menv/bin/Activate.ps1 new file mode 100644 index 00000000..b49d77ba --- /dev/null +++ b/menv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/menv/bin/activate b/menv/bin/activate new file mode 100644 index 00000000..baf9bd95 --- /dev/null +++ b/menv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath "/Users/jennefermaldonado/src/cditools/menv") +else + # use the path as-is + export VIRTUAL_ENV="/Users/jennefermaldonado/src/cditools/menv" +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(menv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(menv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/menv/bin/activate.csh b/menv/bin/activate.csh new file mode 100644 index 00000000..ca51c366 --- /dev/null +++ b/menv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/jennefermaldonado/src/cditools/menv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(menv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(menv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/menv/bin/activate.fish b/menv/bin/activate.fish new file mode 100644 index 00000000..ab6160ad --- /dev/null +++ b/menv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/jennefermaldonado/src/cditools/menv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(menv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(menv) " +end diff --git a/menv/bin/f2py b/menv/bin/f2py new file mode 100755 index 00000000..7860c463 --- /dev/null +++ b/menv/bin/f2py @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/nodeenv b/menv/bin/nodeenv new file mode 100755 index 00000000..ce1593ad --- /dev/null +++ b/menv/bin/nodeenv @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from nodeenv import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/numpy-config b/menv/bin/numpy-config new file mode 100755 index 00000000..1a3379e6 --- /dev/null +++ b/menv/bin/numpy-config @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from numpy._configtool import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/pint-convert b/menv/bin/pint-convert new file mode 100755 index 00000000..77257954 --- /dev/null +++ b/menv/bin/pint-convert @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pint.pint_convert import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/pip b/menv/bin/pip new file mode 100755 index 00000000..c882b375 --- /dev/null +++ b/menv/bin/pip @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/pip3 b/menv/bin/pip3 new file mode 100755 index 00000000..c882b375 --- /dev/null +++ b/menv/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/pip3.12 b/menv/bin/pip3.12 new file mode 100755 index 00000000..c882b375 --- /dev/null +++ b/menv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/py.test b/menv/bin/py.test new file mode 100755 index 00000000..67ca7ea8 --- /dev/null +++ b/menv/bin/py.test @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/menv/bin/pygmentize b/menv/bin/pygmentize new file mode 100755 index 00000000..bfb7f793 --- /dev/null +++ b/menv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/menv/bin/pyright b/menv/bin/pyright new file mode 100755 index 00000000..875ac567 --- /dev/null +++ b/menv/bin/pyright @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pyright.cli import entrypoint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(entrypoint()) diff --git a/menv/bin/pyright-langserver b/menv/bin/pyright-langserver new file mode 100755 index 00000000..400c37a7 --- /dev/null +++ b/menv/bin/pyright-langserver @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pyright.langserver import entrypoint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(entrypoint()) diff --git a/menv/bin/pyright-python b/menv/bin/pyright-python new file mode 100755 index 00000000..875ac567 --- /dev/null +++ b/menv/bin/pyright-python @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pyright.cli import entrypoint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(entrypoint()) diff --git a/menv/bin/pyright-python-langserver b/menv/bin/pyright-python-langserver new file mode 100755 index 00000000..400c37a7 --- /dev/null +++ b/menv/bin/pyright-python-langserver @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pyright.langserver import entrypoint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(entrypoint()) diff --git a/menv/bin/pytest b/menv/bin/pytest new file mode 100755 index 00000000..67ca7ea8 --- /dev/null +++ b/menv/bin/pytest @@ -0,0 +1,8 @@ +#!/Users/jennefermaldonado/src/cditools/menv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/menv/bin/python b/menv/bin/python new file mode 120000 index 00000000..a03c0af8 --- /dev/null +++ b/menv/bin/python @@ -0,0 +1 @@ +/opt/homebrew/anaconda3/bin/python \ No newline at end of file diff --git a/menv/bin/python3 b/menv/bin/python3 new file mode 120000 index 00000000..d8654aa0 --- /dev/null +++ b/menv/bin/python3 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/menv/bin/python3.12 b/menv/bin/python3.12 new file mode 120000 index 00000000..d8654aa0 --- /dev/null +++ b/menv/bin/python3.12 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/menv/pyvenv.cfg b/menv/pyvenv.cfg new file mode 100644 index 00000000..36695c16 --- /dev/null +++ b/menv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /opt/homebrew/anaconda3/bin +include-system-site-packages = false +version = 3.12.7 +executable = /opt/homebrew/anaconda3/bin/python3.12 +command = /opt/homebrew/anaconda3/bin/python -m venv /Users/jennefermaldonado/src/cditools/menv diff --git a/pyrightconfig.json b/pyrightconfig.json index f92b3d6b..b9c8c749 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -2,4 +2,4 @@ "typeCheckingMode": "basic", "reportMissingTypeStubs": false, "reportUntypedBaseClass": false -} \ No newline at end of file +} From 9b0f72d694ed1ae5392b9fdf008f291d56bc402e Mon Sep 17 00:00:00 2001 From: jennmald Date: Tue, 22 Jul 2025 14:24:28 -0400 Subject: [PATCH 28/32] remove env --- menv/bin/Activate.ps1 | 247 ----------------------------- menv/bin/activate | 70 -------- menv/bin/activate.csh | 27 ---- menv/bin/activate.fish | 69 -------- menv/bin/f2py | 8 - menv/bin/nodeenv | 8 - menv/bin/numpy-config | 8 - menv/bin/pint-convert | 8 - menv/bin/pip | 8 - menv/bin/pip3 | 8 - menv/bin/pip3.12 | 8 - menv/bin/py.test | 8 - menv/bin/pygmentize | 8 - menv/bin/pyright | 8 - menv/bin/pyright-langserver | 8 - menv/bin/pyright-python | 8 - menv/bin/pyright-python-langserver | 8 - menv/bin/pytest | 8 - menv/bin/python | 1 - menv/bin/python3 | 1 - menv/bin/python3.12 | 1 - menv/pyvenv.cfg | 5 - 22 files changed, 533 deletions(-) delete mode 100644 menv/bin/Activate.ps1 delete mode 100644 menv/bin/activate delete mode 100644 menv/bin/activate.csh delete mode 100644 menv/bin/activate.fish delete mode 100755 menv/bin/f2py delete mode 100755 menv/bin/nodeenv delete mode 100755 menv/bin/numpy-config delete mode 100755 menv/bin/pint-convert delete mode 100755 menv/bin/pip delete mode 100755 menv/bin/pip3 delete mode 100755 menv/bin/pip3.12 delete mode 100755 menv/bin/py.test delete mode 100755 menv/bin/pygmentize delete mode 100755 menv/bin/pyright delete mode 100755 menv/bin/pyright-langserver delete mode 100755 menv/bin/pyright-python delete mode 100755 menv/bin/pyright-python-langserver delete mode 100755 menv/bin/pytest delete mode 120000 menv/bin/python delete mode 120000 menv/bin/python3 delete mode 120000 menv/bin/python3.12 delete mode 100644 menv/pyvenv.cfg diff --git a/menv/bin/Activate.ps1 b/menv/bin/Activate.ps1 deleted file mode 100644 index b49d77ba..00000000 --- a/menv/bin/Activate.ps1 +++ /dev/null @@ -1,247 +0,0 @@ -<# -.Synopsis -Activate a Python virtual environment for the current PowerShell session. - -.Description -Pushes the python executable for a virtual environment to the front of the -$Env:PATH environment variable and sets the prompt to signify that you are -in a Python virtual environment. Makes use of the command line switches as -well as the `pyvenv.cfg` file values present in the virtual environment. - -.Parameter VenvDir -Path to the directory that contains the virtual environment to activate. The -default value for this is the parent of the directory that the Activate.ps1 -script is located within. - -.Parameter Prompt -The prompt prefix to display when this virtual environment is activated. By -default, this prompt is the name of the virtual environment folder (VenvDir) -surrounded by parentheses and followed by a single space (ie. '(.venv) '). - -.Example -Activate.ps1 -Activates the Python virtual environment that contains the Activate.ps1 script. - -.Example -Activate.ps1 -Verbose -Activates the Python virtual environment that contains the Activate.ps1 script, -and shows extra information about the activation as it executes. - -.Example -Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv -Activates the Python virtual environment located in the specified location. - -.Example -Activate.ps1 -Prompt "MyPython" -Activates the Python virtual environment that contains the Activate.ps1 script, -and prefixes the current prompt with the specified string (surrounded in -parentheses) while the virtual environment is active. - -.Notes -On Windows, it may be required to enable this Activate.ps1 script by setting the -execution policy for the user. You can do this by issuing the following PowerShell -command: - -PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser - -For more information on Execution Policies: -https://go.microsoft.com/fwlink/?LinkID=135170 - -#> -Param( - [Parameter(Mandatory = $false)] - [String] - $VenvDir, - [Parameter(Mandatory = $false)] - [String] - $Prompt -) - -<# Function declarations --------------------------------------------------- #> - -<# -.Synopsis -Remove all shell session elements added by the Activate script, including the -addition of the virtual environment's Python executable from the beginning of -the PATH variable. - -.Parameter NonDestructive -If present, do not remove this function from the global namespace for the -session. - -#> -function global:deactivate ([switch]$NonDestructive) { - # Revert to original values - - # The prior prompt: - if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { - Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt - Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT - } - - # The prior PYTHONHOME: - if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { - Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME - Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME - } - - # The prior PATH: - if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { - Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH - Remove-Item -Path Env:_OLD_VIRTUAL_PATH - } - - # Just remove the VIRTUAL_ENV altogether: - if (Test-Path -Path Env:VIRTUAL_ENV) { - Remove-Item -Path env:VIRTUAL_ENV - } - - # Just remove VIRTUAL_ENV_PROMPT altogether. - if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { - Remove-Item -Path env:VIRTUAL_ENV_PROMPT - } - - # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: - if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { - Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force - } - - # Leave deactivate function in the global namespace if requested: - if (-not $NonDestructive) { - Remove-Item -Path function:deactivate - } -} - -<# -.Description -Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the -given folder, and returns them in a map. - -For each line in the pyvenv.cfg file, if that line can be parsed into exactly -two strings separated by `=` (with any amount of whitespace surrounding the =) -then it is considered a `key = value` line. The left hand string is the key, -the right hand is the value. - -If the value starts with a `'` or a `"` then the first and last character is -stripped from the value before being captured. - -.Parameter ConfigDir -Path to the directory that contains the `pyvenv.cfg` file. -#> -function Get-PyVenvConfig( - [String] - $ConfigDir -) { - Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" - - # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). - $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue - - # An empty map will be returned if no config file is found. - $pyvenvConfig = @{ } - - if ($pyvenvConfigPath) { - - Write-Verbose "File exists, parse `key = value` lines" - $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath - - $pyvenvConfigContent | ForEach-Object { - $keyval = $PSItem -split "\s*=\s*", 2 - if ($keyval[0] -and $keyval[1]) { - $val = $keyval[1] - - # Remove extraneous quotations around a string value. - if ("'""".Contains($val.Substring(0, 1))) { - $val = $val.Substring(1, $val.Length - 2) - } - - $pyvenvConfig[$keyval[0]] = $val - Write-Verbose "Adding Key: '$($keyval[0])'='$val'" - } - } - } - return $pyvenvConfig -} - - -<# Begin Activate script --------------------------------------------------- #> - -# Determine the containing directory of this script -$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition -$VenvExecDir = Get-Item -Path $VenvExecPath - -Write-Verbose "Activation script is located in path: '$VenvExecPath'" -Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" -Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" - -# Set values required in priority: CmdLine, ConfigFile, Default -# First, get the location of the virtual environment, it might not be -# VenvExecDir if specified on the command line. -if ($VenvDir) { - Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" -} -else { - Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." - $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") - Write-Verbose "VenvDir=$VenvDir" -} - -# Next, read the `pyvenv.cfg` file to determine any required value such -# as `prompt`. -$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir - -# Next, set the prompt from the command line, or the config file, or -# just use the name of the virtual environment folder. -if ($Prompt) { - Write-Verbose "Prompt specified as argument, using '$Prompt'" -} -else { - Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" - if ($pyvenvCfg -and $pyvenvCfg['prompt']) { - Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" - $Prompt = $pyvenvCfg['prompt']; - } - else { - Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" - Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" - $Prompt = Split-Path -Path $venvDir -Leaf - } -} - -Write-Verbose "Prompt = '$Prompt'" -Write-Verbose "VenvDir='$VenvDir'" - -# Deactivate any currently active virtual environment, but leave the -# deactivate function in place. -deactivate -nondestructive - -# Now set the environment variable VIRTUAL_ENV, used by many tools to determine -# that there is an activated venv. -$env:VIRTUAL_ENV = $VenvDir - -if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { - - Write-Verbose "Setting prompt to '$Prompt'" - - # Set the prompt to include the env name - # Make sure _OLD_VIRTUAL_PROMPT is global - function global:_OLD_VIRTUAL_PROMPT { "" } - Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT - New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt - - function global:prompt { - Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " - _OLD_VIRTUAL_PROMPT - } - $env:VIRTUAL_ENV_PROMPT = $Prompt -} - -# Clear PYTHONHOME -if (Test-Path -Path Env:PYTHONHOME) { - Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME - Remove-Item -Path Env:PYTHONHOME -} - -# Add the venv to the PATH -Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH -$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/menv/bin/activate b/menv/bin/activate deleted file mode 100644 index baf9bd95..00000000 --- a/menv/bin/activate +++ /dev/null @@ -1,70 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# You cannot run it directly - -deactivate () { - # reset old environment variables - if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then - PATH="${_OLD_VIRTUAL_PATH:-}" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then - PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # Call hash to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - hash -r 2> /dev/null - - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - unset VIRTUAL_ENV_PROMPT - if [ ! "${1:-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -# on Windows, a path can contain colons and backslashes and has to be converted: -if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then - # transform D:\path\to\venv to /d/path/to/venv on MSYS - # and to /cygdrive/d/path/to/venv on Cygwin - export VIRTUAL_ENV=$(cygpath "/Users/jennefermaldonado/src/cditools/menv") -else - # use the path as-is - export VIRTUAL_ENV="/Users/jennefermaldonado/src/cditools/menv" -fi - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/bin:$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - PS1="(menv) ${PS1:-}" - export PS1 - VIRTUAL_ENV_PROMPT="(menv) " - export VIRTUAL_ENV_PROMPT -fi - -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -hash -r 2> /dev/null diff --git a/menv/bin/activate.csh b/menv/bin/activate.csh deleted file mode 100644 index ca51c366..00000000 --- a/menv/bin/activate.csh +++ /dev/null @@ -1,27 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. - -# Created by Davide Di Blasi . -# Ported to Python 3.3 venv by Andrew Svetlov - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' - -# Unset irrelevant variables. -deactivate nondestructive - -setenv VIRTUAL_ENV "/Users/jennefermaldonado/src/cditools/menv" - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/bin:$PATH" - - -set _OLD_VIRTUAL_PROMPT="$prompt" - -if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then - set prompt = "(menv) $prompt" - setenv VIRTUAL_ENV_PROMPT "(menv) " -endif - -alias pydoc python -m pydoc - -rehash diff --git a/menv/bin/activate.fish b/menv/bin/activate.fish deleted file mode 100644 index ab6160ad..00000000 --- a/menv/bin/activate.fish +++ /dev/null @@ -1,69 +0,0 @@ -# This file must be used with "source /bin/activate.fish" *from fish* -# (https://fishshell.com/). You cannot run it directly. - -function deactivate -d "Exit virtual environment and return to normal shell environment" - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - set -e _OLD_FISH_PROMPT_OVERRIDE - # prevents error when using nested fish instances (Issue #93858) - if functions -q _old_fish_prompt - functions -e fish_prompt - functions -c _old_fish_prompt fish_prompt - functions -e _old_fish_prompt - end - end - - set -e VIRTUAL_ENV - set -e VIRTUAL_ENV_PROMPT - if test "$argv[1]" != "nondestructive" - # Self-destruct! - functions -e deactivate - end -end - -# Unset irrelevant variables. -deactivate nondestructive - -set -gx VIRTUAL_ENV "/Users/jennefermaldonado/src/cditools/menv" - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/bin" $PATH - -# Unset PYTHONHOME if set. -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # fish uses a function instead of an env var to generate the prompt. - - # Save the current fish_prompt function as the function _old_fish_prompt. - functions -c fish_prompt _old_fish_prompt - - # With the original prompt function renamed, we can override with our own. - function fish_prompt - # Save the return status of the last command. - set -l old_status $status - - # Output the venv prompt; color taken from the blue of the Python logo. - printf "%s%s%s" (set_color 4B8BBE) "(menv) " (set_color normal) - - # Restore the return status of the previous command. - echo "exit $old_status" | . - # Output the original/"old" prompt. - _old_fish_prompt - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" - set -gx VIRTUAL_ENV_PROMPT "(menv) " -end diff --git a/menv/bin/f2py b/menv/bin/f2py deleted file mode 100755 index 7860c463..00000000 --- a/menv/bin/f2py +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from numpy.f2py.f2py2e import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/nodeenv b/menv/bin/nodeenv deleted file mode 100755 index ce1593ad..00000000 --- a/menv/bin/nodeenv +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from nodeenv import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/numpy-config b/menv/bin/numpy-config deleted file mode 100755 index 1a3379e6..00000000 --- a/menv/bin/numpy-config +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from numpy._configtool import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/pint-convert b/menv/bin/pint-convert deleted file mode 100755 index 77257954..00000000 --- a/menv/bin/pint-convert +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pint.pint_convert import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/pip b/menv/bin/pip deleted file mode 100755 index c882b375..00000000 --- a/menv/bin/pip +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/pip3 b/menv/bin/pip3 deleted file mode 100755 index c882b375..00000000 --- a/menv/bin/pip3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/pip3.12 b/menv/bin/pip3.12 deleted file mode 100755 index c882b375..00000000 --- a/menv/bin/pip3.12 +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/py.test b/menv/bin/py.test deleted file mode 100755 index 67ca7ea8..00000000 --- a/menv/bin/py.test +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pytest import console_main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(console_main()) diff --git a/menv/bin/pygmentize b/menv/bin/pygmentize deleted file mode 100755 index bfb7f793..00000000 --- a/menv/bin/pygmentize +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pygments.cmdline import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/menv/bin/pyright b/menv/bin/pyright deleted file mode 100755 index 875ac567..00000000 --- a/menv/bin/pyright +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pyright.cli import entrypoint -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(entrypoint()) diff --git a/menv/bin/pyright-langserver b/menv/bin/pyright-langserver deleted file mode 100755 index 400c37a7..00000000 --- a/menv/bin/pyright-langserver +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pyright.langserver import entrypoint -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(entrypoint()) diff --git a/menv/bin/pyright-python b/menv/bin/pyright-python deleted file mode 100755 index 875ac567..00000000 --- a/menv/bin/pyright-python +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pyright.cli import entrypoint -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(entrypoint()) diff --git a/menv/bin/pyright-python-langserver b/menv/bin/pyright-python-langserver deleted file mode 100755 index 400c37a7..00000000 --- a/menv/bin/pyright-python-langserver +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pyright.langserver import entrypoint -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(entrypoint()) diff --git a/menv/bin/pytest b/menv/bin/pytest deleted file mode 100755 index 67ca7ea8..00000000 --- a/menv/bin/pytest +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/jennefermaldonado/src/cditools/menv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from pytest import console_main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(console_main()) diff --git a/menv/bin/python b/menv/bin/python deleted file mode 120000 index a03c0af8..00000000 --- a/menv/bin/python +++ /dev/null @@ -1 +0,0 @@ -/opt/homebrew/anaconda3/bin/python \ No newline at end of file diff --git a/menv/bin/python3 b/menv/bin/python3 deleted file mode 120000 index d8654aa0..00000000 --- a/menv/bin/python3 +++ /dev/null @@ -1 +0,0 @@ -python \ No newline at end of file diff --git a/menv/bin/python3.12 b/menv/bin/python3.12 deleted file mode 120000 index d8654aa0..00000000 --- a/menv/bin/python3.12 +++ /dev/null @@ -1 +0,0 @@ -python \ No newline at end of file diff --git a/menv/pyvenv.cfg b/menv/pyvenv.cfg deleted file mode 100644 index 36695c16..00000000 --- a/menv/pyvenv.cfg +++ /dev/null @@ -1,5 +0,0 @@ -home = /opt/homebrew/anaconda3/bin -include-system-site-packages = false -version = 3.12.7 -executable = /opt/homebrew/anaconda3/bin/python3.12 -command = /opt/homebrew/anaconda3/bin/python -m venv /Users/jennefermaldonado/src/cditools/menv From a6299a32f504954062f4b7cb13b26d5e66fa9d1c Mon Sep 17 00:00:00 2001 From: jennmald Date: Thu, 24 Jul 2025 09:51:44 -0400 Subject: [PATCH 29/32] add suggestions for makedirs and fix a merge conflict error --- src/cditools/eiger.py | 4 ++-- src/cditools/merlin.py | 4 ++-- src/cditools/trigger_mixins.py | 2 +- src/cditools/utils.py | 20 -------------------- 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/cditools/eiger.py b/src/cditools/eiger.py index acda7932..33ec826e 100644 --- a/src/cditools/eiger.py +++ b/src/cditools/eiger.py @@ -88,8 +88,8 @@ def stage(self) -> list[object]: # type: ignore[reportIncompatibleMethodOverrid file_prefix = PurePath(self.file_path.get()) / res_uid self._fn = file_prefix - images_per_file = self.file_write_images_per_file.get() - resource_kwargs = {"images_per_file": images_per_file} + images_per_file: str = self.file_write_images_per_file.get() + resource_kwargs: dict[str, str] = {"images_per_file": images_per_file} self._generate_resource(resource_kwargs) diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 8ca1734b..438ed36e 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -19,9 +19,9 @@ from ophyd.areadetector import EpicsSignalWithRBV from ophyd.areadetector.base import ADComponent from ophyd.areadetector.filestore_mixins import FileStorePluginBase, FileStoreTIFF +from ophyd.utils.paths import makedirs from .trigger_mixins import CDIModalTrigger, FileStoreBulkReadable -from .utils import makedirs logger = logging.getLogger(__name__) @@ -83,7 +83,7 @@ def stage(self) -> object: staged = super().stage() logger.info("Staging step 2") res_kwargs = {"frame_per_point": 1} - logger.info("res_kwargs = {frame_per_point: }") + logger.info(f"res_kwargs = {{frame_per_point: {res_kwargs['frame_per_point']}}}") logger.debug("Inserting resource with filename %s", self._fn) logger.info("Inserting resource with filename %s", self._fn) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index b9fbaab7..192fd234 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -33,7 +33,7 @@ def __init__(self, *args: object, **kwargs: object) -> None: class CDIModalSettings(Device): mode = Cpt(Signal, value="internal", doc="Triggering mode (internal/external)") scan_type = Cpt(Signal, value="step", doc="Scan type (step/fly)") - make_directories = Cpt(Signal, value=True, doc="Make directories on the DAQ side") + make_directories = Cpt(Signal, value=False, doc="Make directories on the DAQ side") total_points = Cpt( Signal, value=2, doc="The total number of points to acquire overall" ) diff --git a/src/cditools/utils.py b/src/cditools/utils.py index 6af472c8..f7c1c0a7 100644 --- a/src/cditools/utils.py +++ b/src/cditools/utils.py @@ -5,26 +5,6 @@ from typing import Any -def makedirs(path: str, mode: int = 0o777) -> list[str]: - """Recursively make directories and set permissions""" - # Permissions not working with os.makedirs - - # See: http://stackoverflow.com/questions/5231901 - if not path or Path(path).exists(): - return [] - - head, _ = os.path.split(path) - ret = makedirs(head, mode) - try: - Path(path).mkdir() - except OSError as ex: - if "File exists" not in str(ex): - raise - - Path(path).chmod(mode) - ret.append(path) - return ret - - def ordered_dict_move_to_beginning(od: dict[str, Any], key: str) -> None: if key not in od: return From 24323a1f67852c273a0e5a78fd67bcb669d876d7 Mon Sep 17 00:00:00 2001 From: jennmald Date: Thu, 24 Jul 2025 11:03:10 -0400 Subject: [PATCH 30/32] fix pre-commit --- README.md | 8 ++++++++ src/cditools/merlin.py | 2 +- src/cditools/trigger_mixins.py | 2 +- src/cditools/utils.py | 2 -- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bab6a37c..d719b46b 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,11 @@ [rtd-link]: https://cditools.readthedocs.io/en/latest/?badge=latest + +## Pyright Configuration + +The `pyrightconfig.json` file configures the Pyright type checker for this +project. It sets the type checking mode to "basic" for less strict analysis and +disables warnings about missing type stubs and untyped base classes. This helps +minimize unnecessary alerts from third-party libraries that lack type +information, allowing you to focus on type issues within your own codebase. diff --git a/src/cditools/merlin.py b/src/cditools/merlin.py index 438ed36e..fd12a039 100644 --- a/src/cditools/merlin.py +++ b/src/cditools/merlin.py @@ -83,7 +83,7 @@ def stage(self) -> object: staged = super().stage() logger.info("Staging step 2") res_kwargs = {"frame_per_point": 1} - logger.info(f"res_kwargs = {{frame_per_point: {res_kwargs['frame_per_point']}}}") + logger.info("res_kwargs = {frame_per_point: %s}", res_kwargs["frame_per_point"]) logger.debug("Inserting resource with filename %s", self._fn) logger.info("Inserting resource with filename %s", self._fn) diff --git a/src/cditools/trigger_mixins.py b/src/cditools/trigger_mixins.py index 192fd234..9c93b46d 100644 --- a/src/cditools/trigger_mixins.py +++ b/src/cditools/trigger_mixins.py @@ -152,7 +152,7 @@ def trigger_internal(self) -> DeviceStatus: def trigger_external(self) -> DeviceStatus: if self._staged != Staged.yes: msg = ( - "This detector is not ready to trigger." + "This detector is not ready to trigger. " "Call the stage() method before triggering." ) raise RuntimeError(msg) diff --git a/src/cditools/utils.py b/src/cditools/utils.py index f7c1c0a7..95af49d2 100644 --- a/src/cditools/utils.py +++ b/src/cditools/utils.py @@ -1,7 +1,5 @@ from __future__ import annotations -import os -from pathlib import Path from typing import Any From 714f535a8d94f7dba8f02665ad6dbae5f3356bde Mon Sep 17 00:00:00 2001 From: Jennefer Maldonado <64480998+jennmald@users.noreply.github.com> Date: Thu, 31 Jul 2025 10:24:04 -0400 Subject: [PATCH 31/32] Update README.md Co-authored-by: Max Rakitin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d719b46b..6110006c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ## Pyright Configuration -The `pyrightconfig.json` file configures the Pyright type checker for this +The `pyrightconfig.json` file configures the [Pyright type checker](https://github.com/microsoft/pyright) for this project. It sets the type checking mode to "basic" for less strict analysis and disables warnings about missing type stubs and untyped base classes. This helps minimize unnecessary alerts from third-party libraries that lack type From 7c25a2750fb341de698dc0dc62eaef6276874292 Mon Sep 17 00:00:00 2001 From: jennmald Date: Thu, 31 Jul 2025 10:33:12 -0400 Subject: [PATCH 32/32] fix prettier --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6110006c..8949b6bf 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,9 @@ ## Pyright Configuration -The `pyrightconfig.json` file configures the [Pyright type checker](https://github.com/microsoft/pyright) for this -project. It sets the type checking mode to "basic" for less strict analysis and -disables warnings about missing type stubs and untyped base classes. This helps -minimize unnecessary alerts from third-party libraries that lack type -information, allowing you to focus on type issues within your own codebase. +The `pyrightconfig.json` file configures the +[Pyright type checker](https://github.com/microsoft/pyright) for this project. +It sets the type checking mode to "basic" for less strict analysis and disables +warnings about missing type stubs and untyped base classes. This helps minimize +unnecessary alerts from third-party libraries that lack type information, +allowing you to focus on type issues within your own codebase.