From 2acac64bef296c75cfc0aa758a88dc29a9174406 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 09:59:04 -0500 Subject: [PATCH 01/24] update tests --- .github/workflows/ci.yml | 28 +++++++++++++--------------- setup.cfg | 8 ++++---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed21aff..36c7ea1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,8 @@ name: CI on: push: - branches: - - main - tags: - - "v*" + branches: [main] + tags: [v*] pull_request: workflow_dispatch: @@ -19,14 +17,14 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, '3.10'] - platform: [ubuntu-latest] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + platform: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: tlambert03/setup-qt-libs@v1 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true python-version: ${{ matrix.python-version }} @@ -37,16 +35,16 @@ jobs: - name: Install dependencies run: | mamba install pyopencl pocl - sudo apt-get install -y xvfb - pip install -U pip wheel - pip install -e .[tests] + pip install -U pip + pip install -e .[test] - - name: Test with xvfb - if: runner.os == 'Linux' - run: xvfb-run --auto-servernum pytest --cov llspy + - name: Test + uses: aganders3/headless-gui@v2 + with: + run: pytest --cov llspy - name: Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 deploy: name: Deploy diff --git a/setup.cfg b/setup.cfg index d416edc..5ee5e33 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,11 +18,11 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Topic :: Scientific/Engineering Topic :: Scientific/Engineering :: Visualization @@ -40,8 +40,8 @@ install_requires = tifffile voluptuous watchdog - numba;python_version < '3.10' -python_requires = >=3.6 + numba;python_version < '3.12' +python_requires = >=3.7 include_package_data = True zip_safe = False @@ -65,7 +65,7 @@ pyside = PySide2 spimagine = spimagine -tests = +test = PyQt5 pytest pytest-cov From 31c9f44f259a540d2424fd5c782f052a2a86b554 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 10:06:48 -0500 Subject: [PATCH 02/24] autoupdate --- .pre-commit-config.yaml | 14 +++++++------- README.rst | 6 +++--- fiducialreg/imref.py | 1 - llspy/__main__.py | 1 - llspy/camcalib.py | 2 -- llspy/camera.py | 1 - llspy/gui/camcalibgui.py | 2 -- llspy/gui/img_dialog.py | 3 --- llspy/gui/mainwindow.py | 4 ---- llspy/gui/qtlogger.py | 1 - llspy/gui/workers.py | 6 ------ llspy/libcudawrapper.py | 1 - llspy/llsdir.py | 1 - llspy/xzpsf.py | 1 - setup.cfg | 9 ++------- 15 files changed, 12 insertions(+), 41 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8dfe5d7..ee01a94 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,34 +1,34 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.5.0 hooks: - id: check-docstring-first - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.20.1 + rev: v2.5.0 hooks: - id: setup-cfg-fmt - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 6.1.0 hooks: - id: flake8 additional_dependencies: [flake8-typing-imports==1.7.0] - repo: https://github.com/myint/autoflake - rev: v1.4 + rev: v2.2.1 hooks: - id: autoflake args: ["--in-place", "--remove-all-unused-imports"] - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.13.0 hooks: - id: isort - repo: https://github.com/psf/black - rev: 22.6.0 + rev: 23.11.0 hooks: - id: black - repo: https://github.com/asottile/pyupgrade - rev: v2.37.1 + rev: v3.15.0 hooks: - id: pyupgrade args: [--py37-plus, --keep-runtime-typing] diff --git a/README.rst b/README.rst index 30aa79b..7b68c7d 100755 --- a/README.rst +++ b/README.rst @@ -173,8 +173,8 @@ Installation **Note**: *As of version 0.4.2 cudaDecon is now included in the LLSpy conda package and requires no additional steps for installation. Horray for open source!* -#. Install `Anaconda `_ or `Miniconda `_ -#. Launch a ``terminal`` window (OS X, Linux), or ``Anaconda Prompt`` (Windows) +#. Install `conda `_ +#. Launch a ``terminal`` window (Linux), or ``Anaconda Prompt`` (Windows) #. Add the "conda-forge" and "talley" channels to your conda config .. code:: bash @@ -186,7 +186,7 @@ Installation .. code:: bash - $ conda create -n llsenv python=3.6 llspy + $ conda create -n llsenv python=3.11 llspy $ conda activate llsenv The ``create -n llsenv`` line creates a virtual environment. This is optional, but recommended as it easier to uninstall cleanly and prevents conflicts with any other python environments. If installing into a virtual environment, you must source the environment before proceeding, and each time before using llspy. diff --git a/fiducialreg/imref.py b/fiducialreg/imref.py index b2a9ff8..6479a27 100644 --- a/fiducialreg/imref.py +++ b/fiducialreg/imref.py @@ -18,7 +18,6 @@ class DimensionManager: def __init__( self, DimensionName="", NumberOfSamples=2, Delta=1, StartCoordinateInWorld=0.5 ): - if DimensionName not in ["X", "Y", "Z"]: raise ValueError("DimensionName must be X, Y, or Z") self.DimensionName = DimensionName diff --git a/llspy/__main__.py b/llspy/__main__.py index 551f97a..3dd0233 100644 --- a/llspy/__main__.py +++ b/llspy/__main__.py @@ -9,7 +9,6 @@ from llspy.bin.llspy_gui import main if __name__ == "__main__": - import logging logger = logging.getLogger(__name__) diff --git a/llspy/camcalib.py b/llspy/camcalib.py index 7be9030..d31c10a 100644 --- a/llspy/camcalib.py +++ b/llspy/camcalib.py @@ -168,7 +168,6 @@ def process_dark_images(folder, callback=None, callback2=None): def process_bright_images(folder, darkavg, darkstd, callback=None, save=True): - ch0list, ch1list = get_channel_list(folder) pre, post = combine_stacks(ch0list, ch1list, darkavg) @@ -203,7 +202,6 @@ def process_bright_images(folder, darkavg, darkstd, callback=None, save=True): if __name__ == "__main__": - # this script assumes you have aquired a series of 2-channel zstacks # (not actually a stack, turn off Z galvo, and Z and Sample Piezos # the first channel is "bright" and "even" (such as 488 laser sheet exciting FITC) diff --git a/llspy/camera.py b/llspy/camera.py index d1494ef..eaefc2f 100644 --- a/llspy/camera.py +++ b/llspy/camera.py @@ -364,7 +364,6 @@ def correct_stacks( if __name__ == "__main__": - from llspy import llsdir, samples paramfile = samples.camparams # path to the calibration file diff --git a/llspy/gui/camcalibgui.py b/llspy/gui/camcalibgui.py index e9e3095..1097da7 100644 --- a/llspy/gui/camcalibgui.py +++ b/llspy/gui/camcalibgui.py @@ -126,7 +126,6 @@ def setFolder(self): self.darkSTDineEdit.setText(os.path.join(folder, "dark_STD.tif")) def processFolder(self): - folder = self.camCalibFolderLineEdit.text() darkavg = None @@ -214,7 +213,6 @@ def _abort(self): if __name__ == "__main__": - app = QtW.QApplication(sys.argv) # dlg = LogWindow() # dlg.show() diff --git a/llspy/gui/img_dialog.py b/llspy/gui/img_dialog.py index 2983e03..473bf85 100644 --- a/llspy/gui/img_dialog.py +++ b/llspy/gui/img_dialog.py @@ -73,7 +73,6 @@ def __init__(self, name, wave=None, parent=None): class DataModel(QtCore.QObject): - _idxChanged = QtCore.Signal() _dataChanged = QtCore.Signal() @@ -294,7 +293,6 @@ def __getitem__(self, tczTuple): class MplCanvas(FigureCanvas): - _contrastChanged = QtCore.Signal() def __init__(self): @@ -582,7 +580,6 @@ def update_sliders(self): self.update_axis_slider(axis, n) def initialize(self): - datamax = self.data.max() datamin = self.data.min() # dataRange = datamax - datamin diff --git a/llspy/gui/mainwindow.py b/llspy/gui/mainwindow.py index 6ca5d1b..64316c6 100644 --- a/llspy/gui/mainwindow.py +++ b/llspy/gui/mainwindow.py @@ -1138,14 +1138,12 @@ def displayPreview(self, array, dx, dz, params=None): viewer.dims.ndisplay = 3 self.spimwins.append(viewer) elif self.prevBackendSpimagineRadio.isChecked() and _SPIMAGINE_IMPORTED: - if np.squeeze(array).ndim > 4: arrays = [array[:, i] for i in range(array.shape[1])] else: arrays = [np.squeeze(array)] for arr in arrays: - datamax = arr.max() datamin = arr.min() dataRange = datamax - datamin @@ -1660,7 +1658,6 @@ def concatenateSelected(self): [self.listbox.addPath(p) for p in selectedPaths] def undoRenameSelected(self): - box = QtW.QMessageBox() box.setWindowTitle("Undo Renaming") box.setText( @@ -1712,7 +1709,6 @@ def renameSelected(self): [self.listbox.addPath(osp.join(item, p)) for p in os.listdir(item)] def mergeMIPtool(self): - if len(self.listbox.selectedPaths()): for obj in self.listbox.selectedObjects(): obj.mergemips() diff --git a/llspy/gui/qtlogger.py b/llspy/gui/qtlogger.py index ef0d78f..b2d3123 100644 --- a/llspy/gui/qtlogger.py +++ b/llspy/gui/qtlogger.py @@ -27,7 +27,6 @@ def formatException(self, exc_info): class NotificationHandler(QObject, logging.Handler): - emitSignal = Signal(str) def __init__(self): diff --git a/llspy/gui/workers.py b/llspy/gui/workers.py index 7878216..42a9042 100644 --- a/llspy/gui/workers.py +++ b/llspy/gui/workers.py @@ -154,7 +154,6 @@ def onFinished(self, exitCode, exitStatus): class CompressionWorker(SubprocessWorker): - status_update = QtCore.Signal(str, int) def __init__(self, path, mode="compress", binary=None, wid=1, **kwargs): @@ -235,7 +234,6 @@ def untar(self, tarball, delete=True): with tarfile.open(tarball) as tar: def is_within_directory(directory, target): - abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) @@ -244,7 +242,6 @@ def is_within_directory(directory, target): return prefix == abs_directory def safe_extract(tar, path=".", members=None, *, numeric_owner=False): - for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): @@ -345,7 +342,6 @@ def split(a, n): class LLSitemWorker(QtCore.QObject): - sig_starting_item = QtCore.Signal(str, int) # item path, numfiles status_update = QtCore.Signal(str) # update mainGUI status @@ -438,7 +434,6 @@ def work(self): # only call cudaDeconv if we need to deskew or deconvolve if self.P.nIters > 0 or (self.P.deskew != 0 and self.P.saveDeskewedRaw): - try: # check the binary path and create object binary = llspy.cudabinwrapper.CUDAbin(_CUDABIN) @@ -547,7 +542,6 @@ def on_CUDAworker_done(self, worker_id): self.post_process() def post_process(self): - if self.P.doReg: self.status_update.emit(f"Doing Channel Registration: {self.E.basename}") try: diff --git a/llspy/libcudawrapper.py b/llspy/libcudawrapper.py index d18bc09..528fd7d 100644 --- a/llspy/libcudawrapper.py +++ b/llspy/libcudawrapper.py @@ -417,7 +417,6 @@ def RL_decon( plt.show() RL_cleanup() elif sys.argv[1] == "camcor": - import time from llspy import llsdir, samples diff --git a/llspy/llsdir.py b/llspy/llsdir.py index 6915b52..5bf5973 100644 --- a/llspy/llsdir.py +++ b/llspy/llsdir.py @@ -1226,7 +1226,6 @@ def median_and_trim( trimX=(0, 0), **kwargs, ): - trim = (trimZ, trimY, trimX) outpath = self.path.joinpath("Corrected") diff --git a/llspy/xzpsf.py b/llspy/xzpsf.py index 320a464..96bfa16 100755 --- a/llspy/xzpsf.py +++ b/llspy/xzpsf.py @@ -80,7 +80,6 @@ def find_settext(path, filepattern="*Settings.txt"): if __name__ == "__main__": - import argparse parser = argparse.ArgumentParser() diff --git a/setup.cfg b/setup.cfg index 5ee5e33..074a69d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ url = https://github.com/tlambert03/LLSpy author = Talley Lambert author_email = talley.lambert@gmail.com license = BSD 3-clause -license_file = LICENSE +license_files = LICENSE classifiers = Development Status :: 3 - Alpha Intended Audience :: Science/Research @@ -18,11 +18,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 Topic :: Scientific/Engineering Topic :: Scientific/Engineering :: Visualization @@ -41,7 +36,7 @@ install_requires = voluptuous watchdog numba;python_version < '3.12' -python_requires = >=3.7 +python_requires = >=3.8 include_package_data = True zip_safe = False From f7763f6f23c9208dac1ea231e0e4033cd8013a73 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 10:14:35 -0500 Subject: [PATCH 03/24] switch to ruff --- .pre-commit-config.yaml | 41 ++++++++++++----------- fiducialreg/fiducialreg.py | 36 +++++++------------- fiducialreg/imref.py | 16 ++++----- llspy/arrayfun.py | 2 +- llspy/bin/llspy_cli.py | 14 ++++---- llspy/bin/llspy_gui.py | 4 +-- llspy/camera.py | 14 ++++---- llspy/cudabinwrapper.py | 4 +-- llspy/gui/camcalibgui.py | 4 +-- llspy/gui/helpers.py | 2 +- llspy/gui/img_dialog.py | 12 +++---- llspy/gui/mainwindow.py | 39 ++++++++++++---------- llspy/gui/qtlogger.py | 2 +- llspy/gui/workers.py | 20 ++++-------- llspy/libcudawrapper.py | 2 +- llspy/libinstall.py | 8 ++--- llspy/llsdir.py | 67 ++++++++++++++------------------------ llspy/otf.py | 6 ++-- llspy/samples.py | 26 ++++++--------- llspy/schema.py | 4 +-- llspy/util.py | 12 ++++--- pyproject.toml | 26 +++++++++++++++ setup.cfg | 10 ------ tests/conftest.py | 1 - tests/test_gui.py | 1 - tests/test_llsdir.py | 7 ++-- tests/test_processing.py | 3 +- 27 files changed, 180 insertions(+), 203 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee01a94..62f6461 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,8 @@ +ci: + autoupdate_schedule: monthly + autofix_commit_msg: "style(pre-commit.ci): auto fixes [...]" + autoupdate_commit_msg: "ci(pre-commit.ci): autoupdate" + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 @@ -5,34 +10,32 @@ repos: - id: check-docstring-first - id: end-of-file-fixer - id: trailing-whitespace + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.15 + hooks: + - id: validate-pyproject - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.5.0 hooks: - id: setup-cfg-fmt - - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 - hooks: - - id: flake8 - additional_dependencies: [flake8-typing-imports==1.7.0] - - repo: https://github.com/myint/autoflake - rev: v2.2.1 + + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.1.7 hooks: - - id: autoflake - args: ["--in-place", "--remove-all-unused-imports"] - - repo: https://github.com/PyCQA/isort - rev: 5.13.0 - hooks: - - id: isort + - id: ruff + args: [--fix, --unsafe-fixes] + - repo: https://github.com/psf/black rev: 23.11.0 hooks: - id: black - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 - hooks: - - id: pyupgrade - args: [--py37-plus, --keep-runtime-typing] + # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: v0.812 + # rev: v1.6.1 # hooks: # - id: mypy + # files: "^src/" + # # # you have to add the things you want to type check against here + # # additional_dependencies: + # # - numpy diff --git a/fiducialreg/fiducialreg.py b/fiducialreg/fiducialreg.py index 7b3ed7e..7e146b3 100644 --- a/fiducialreg/fiducialreg.py +++ b/fiducialreg/fiducialreg.py @@ -71,7 +71,7 @@ def log_filter(img, blurxysigma=1, blurzsigma=2.5, mask=None): def bead_centroids(img, labeled, nlabels): # get center of mass of each object - return [ndimage.center_of_mass(img, labeled, l) for l in range(1, nlabels + 1)] + return [ndimage.center_of_mass(img, labeled, x) for x in range(1, nlabels + 1)] def get_thresh(im, mincount=None, steps=100): @@ -100,9 +100,7 @@ def get_thresh(im, mincount=None, steps=100): ) modecount = stats.mode(object_count[(object_count >= mincount)], axis=None).mode[0] logging.debug( - "Threshold detected: {}".format( - threshrange[np.argmax(object_count == modecount)] - ) + f"Threshold detected: {threshrange[np.argmax(object_count == modecount)]}" ) return threshrange[np.argmax(object_count == modecount)], modecount @@ -355,7 +353,7 @@ def FitModelWeighted(modelFcn, startParameters, data, sigmas, *args): return optimize.leastsq( weightedMissfitF, startParameters, - (modelFcn, data.ravel(), (1.0 / sigmas).astype("f").ravel()) + args, + (modelFcn, data.ravel(), (1.0 / sigmas).astype("f").ravel(), *args), full_output=1, ) @@ -845,9 +843,7 @@ def get_all_tforms( regto.append(ref) else: logger.warning( - "Reference {} not in lablels: {} ... skipping".format( - ref, self.labels - ) + f"Reference {ref} not in lablels: {self.labels} ... skipping" ) if not len(regto): logger.error("No recognized values in refs list. No tforms calculated") @@ -881,9 +877,7 @@ def get_all_tforms( except Exception: print("SKIPPING MODE: ", mode) logger.error( - 'Failed to calculate mode "{}" in get_all_tforms. Skipping.'.format( - mode - ) + f'Failed to calculate mode "{mode}" in get_all_tforms. Skipping.' ) return D @@ -901,7 +895,7 @@ def default(self, obj): if not all(isinstance(i, np.ndarray) for i in obj): return obj.tolist() nestedList = obj.tolist() - return [self.fixedString(l) for l in nestedList] + return [self.fixedString(x) for x in nestedList] return json.JSONEncoder.default(self, obj) tforms = self.get_all_tforms(**kwargs) @@ -946,17 +940,13 @@ def tform( movIdx = self.labels.index(movingLabel) except ValueError: raise ValueError( - "Could not find label {} in reg list: {}".format( - movingLabel, self.labels - ) + f"Could not find label {movingLabel} in reg list: {self.labels}" ) try: fixIdx = self.labels.index(fixedLabel) except ValueError: raise ValueError( - "Could not find label {} in reg list: {}".format( - fixedLabel, self.labels - ) + f"Could not find label {fixedLabel} in reg list: {self.labels}" ) mode = mode.lower() @@ -1159,9 +1149,9 @@ def parsefile(self): if mov not in self.tform_dict[ref]: self.tform_dict[ref][mov] = {} self.tform_dict[ref][mov][mode] = tform["tform"] - self.refwaves = sorted(list(set(self.refwaves))) - self.movwaves = sorted(list(set(self.movwaves))) - self.modes = sorted(list(set(self.modes))) + self.refwaves = sorted(set(self.refwaves)) + self.movwaves = sorted(set(self.movwaves)) + self.modes = sorted(set(self.modes)) self.waves = self.refwaves # to make it easier to substitute for RegDir @property @@ -1180,9 +1170,7 @@ def get_tform(self, moving, ref, mode): raise RegistrationError(f"Reference wave {ref} not in registration file") if moving not in self.tform_dict[ref]: raise RegistrationError( - "No transform to map moving wave {} onto refrence wave {}".format( - moving, ref - ) + f"No transform to map moving wave {moving} onto refrence wave {ref}" ) if mode not in self.tform_dict[ref][moving]: raise RegistrationError( diff --git a/fiducialreg/imref.py b/fiducialreg/imref.py index 6479a27..d0e2ab6 100644 --- a/fiducialreg/imref.py +++ b/fiducialreg/imref.py @@ -121,13 +121,13 @@ def __init__(self, *args): self.Dimension.X = DimensionManager("X", args[0][1]) self.Dimension.Y = DimensionManager("Y", args[0][0]) elif len(args) == 3: - if all([isinstance(x, (list, np.ndarray)) for x in args[1:3]]): + if all(isinstance(x, (list, np.ndarray)) for x in args[1:3]): # world limits provided self.Dimension.X = DimensionManager("X", args[0][1]) self.Dimension.Y = DimensionManager("Y", args[0][0]) self.XWorldLimits = args[1] self.YWorldLimits = args[2] - elif all([np.isscalar(x) for x in args[1:3]]): + elif all(np.isscalar(x) for x in args[1:3]): # imref2d(imageSize,pixelExtentInWorldX,pixelExtentInWorldY) self.Dimension.X = DimensionManager( "X", args[0][1], args[1], args[1] / 2 @@ -218,7 +218,7 @@ def worldToIntrinsic(self, xWorld, yWorld): def worldToSubscript(self, xWorld, yWorld): if len({type(n) for n in (xWorld, yWorld)}) > 1: raise ValueError("All inputs to worldToSubscript must have same type") - if not any([np.isscalar(n) for n in (xWorld, yWorld)]): + if not any(np.isscalar(n) for n in (xWorld, yWorld)): if len({len(n) for n in (xWorld, yWorld)}) > 1: raise ValueError("All inputs to worldToSubscript must have same size") # TODO: check order of CRP output index @@ -246,7 +246,7 @@ def __repr__(self): if np.issubdtype(val.dtype, np.int): repdict[n] = list(val) else: - repdict[n] = list(f"{i:.4f}" for i in val) + repdict[n] = [f"{i:.4f}" for i in val] elif isinstance(self.__getattribute__(n), (np.float, float)): repdict[n] = f"{self.__getattribute__(n):.4f}" elif isinstance(self.__getattribute__(n), (np.int, np.int64, int)): @@ -287,7 +287,7 @@ def __init__(self, *args): self.Dimension.Y = DimensionManager("Y", args[0][1]) self.Dimension.Z = DimensionManager("Z", args[0][0]) elif len(args) == 4: - if all([isinstance(x, (list, np.ndarray)) for x in args[1:4]]): + if all(isinstance(x, (list, np.ndarray)) for x in args[1:4]): # imref3d(imageSize,pixelExtentInWorldX,pixelExtentInWorldY,pixelExtentInWorldZ) self.Dimension.X = DimensionManager("X", args[0][2]) self.Dimension.Y = DimensionManager("Y", args[0][1]) @@ -295,7 +295,7 @@ def __init__(self, *args): self.XWorldLimits = args[1] self.YWorldLimits = args[2] self.ZWorldLimits = args[3] - elif all([np.isscalar(x) for x in args[1:4]]): + elif all(np.isscalar(x) for x in args[1:4]): # imref3d(imageSize,xWorldLimits,yWorldLimits,zWorldLimits) self.Dimension.X = DimensionManager( "X", args[0][2], args[1], args[1] / 2 @@ -376,7 +376,7 @@ def worldToIntrinsic(self, xWorld, yWorld, zWorld): def worldToSubscript(self, xWorld, yWorld, zWorld): if len({type(n) for n in (xWorld, yWorld, zWorld)}) > 1: raise ValueError("All inputs to worldToSubscript must have same type") - if not any([np.isscalar(n) for n in (xWorld, yWorld, zWorld)]): + if not any(np.isscalar(n) for n in (xWorld, yWorld, zWorld)): if len({len(n) for n in (xWorld, yWorld, zWorld)}) > 1: raise ValueError("All inputs to worldToSubscript must have same size") # TODO: check order of CRP output index @@ -394,7 +394,7 @@ def worldToSubscript(self, xWorld, yWorld, zWorld): return c, r, p - def sizesMatch(self, I): + def sizesMatch(self, x): raise NotImplementedError() # imageSize = I.shape # if ~isequal(size(self.ImageSize), size(imageSize)) diff --git a/llspy/arrayfun.py b/llspy/arrayfun.py index 6532173..5c9240d 100644 --- a/llspy/arrayfun.py +++ b/llspy/arrayfun.py @@ -38,7 +38,7 @@ def threshold_li(image): raise ValueError( "threshold_li is expected to work with images " "having more than one value. The input image seems " - "to have just one value {}.".format(image.flat[0]) + f"to have just one value {image.flat[0]}." ) # Copy to ensure input image is not modified diff --git a/llspy/bin/llspy_cli.py b/llspy/bin/llspy_cli.py index ef3a324..cd73435 100644 --- a/llspy/bin/llspy_cli.py +++ b/llspy/bin/llspy_cli.py @@ -29,7 +29,7 @@ DEFAULTS = schema.__defaults__ -CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) +CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]} class Config(dict): @@ -106,13 +106,13 @@ def print_cfgfile(self): def update_default(self, key, value): if key not in DEFAULTS: click.secho( - "{} is not a recognized parameter! use config --info to " - "list all recognized parameters".format(key), + f"{key} is not a recognized parameter! use config --info to " + "list all recognized parameters", fg="red", ) return 0 try: - key, value = list(schema.validateItems(**{key: value}).items())[0] + key, value = next(iter(schema.validateItems(**{key: value}).items())) except Exception as e: click.secho(str(e), fg="red") return 0 @@ -123,7 +123,7 @@ def update_default(self, key, value): # preserve comments with open(self.default_path) as f: comments = [ - l for l in list(f) if l.startswith(self.comment) and key not in l + x for x in list(f) if x.startswith(self.comment) and key not in x ] parser = configparser.ConfigParser(allow_no_value=True) @@ -244,7 +244,7 @@ def info(paths, verbose, recurse, depth, showsize): [paths.insert(i, s) for s in reversed(subf)] # remove duplicates - paths = sorted(list(set(paths))) + paths = sorted(set(paths)) if verbose == 0 and len(paths): click.echo() @@ -849,7 +849,7 @@ def compress( [paths.insert(i, s) for s in reversed(subf)] # remove duplicates - paths = sorted(list(set(paths))) + paths = sorted(set(paths)) if dryrun: click.secho("DRY RUN: NOTHING PERFORMED!", fg="red", underline=True) diff --git a/llspy/bin/llspy_gui.py b/llspy/bin/llspy_gui.py index 2e8d272..feec003 100644 --- a/llspy/bin/llspy_gui.py +++ b/llspy/bin/llspy_gui.py @@ -159,9 +159,9 @@ def setOptOut(value): QtWidgets.QMessageBox.information( mainGUI, "Newer Version Available!", - "Update available: v%s\n\nYou are using v%s\n\nIf you are using " + "Update available: v{}\n\nYou are using v{}\n\nIf you are using " 'anaconda, you may update by typing "conda update -c talley llspy" ' - "at the anaconda prompt" % (newestVersion, llspy.__version__), + "at the anaconda prompt".format(newestVersion, llspy.__version__), QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton, ) diff --git a/llspy/camera.py b/llspy/camera.py index eaefc2f..f3851b1 100644 --- a/llspy/camera.py +++ b/llspy/camera.py @@ -239,17 +239,17 @@ def __init__(self, fname=config.__CAMPARAMS__, data=None, roi=None): if not self.shape[0] >= 3: raise ValueError( "Camera parameter file must have at least " - "3 planes. {} has only {}".format(fname, self.shape[0]) + f"3 planes. {fname} has only {self.shape[0]}" ) if not self.roi.width == self.shape[1]: raise ValueError( "Tiff file provided does not have the same width " - "({}) as the proivded roi ({})".format(self.shape[1], self.roi.width) + f"({self.shape[1]}) as the proivded roi ({self.roi.width})" ) if not self.roi.height == self.shape[2]: raise ValueError( "Tiff file provided does not have the same height " - "({}) as the proivded roi ({})".format(self.shape[2], self.roi.height) + f"({self.shape[2]}) as the proivded roi ({self.roi.height})" ) self.width = self.roi.width self.height = self.roi.height @@ -303,7 +303,7 @@ def correct_stacks( raise ValueError(f"Empty list of stacks received: {stacks}") if len({S.shape for S in stacks}) > 1: raise ValueError("All stacks in list must have the same shape") - if not all([isinstance(S, np.ndarray) for S in stacks]): + if not all(isinstance(S, np.ndarray) for S in stacks): raise ValueError("All stacks in list must be of type: np.ndarray") # interleave stacks into single 3D so that they are in the order: @@ -332,7 +332,7 @@ def correct_stacks( else: raise ValueError( "unrecognized value for flashCorrectTarget " - "parameter: {}".format(flashCorrectTarget) + f"parameter: {flashCorrectTarget}" ) # interleaved = np.subtract(interleaved, self.offset) @@ -348,14 +348,14 @@ def correct_stacks( # (particularly if an object is truncated and there's more content # just off to the side of the camera ROI) # this will delete the edge columns - if any([any(i) for i in trim]): + if any(any(i) for i in trim): interleaved = arrayfun.trimedges(interleaved, trim, numStacks) if not np.issubdtype(interleaved.dtype, typ): warnings.warn("CONVERTING") interleaved = interleaved.astype(typ) - deinterleaved = [s for s in np.split(interleaved, interleaved.shape[0])] + deinterleaved = list(np.split(interleaved, interleaved.shape[0])) deinterleaved = [ np.concatenate(deinterleaved[q::numStacks]) for q in range(numStacks) ] diff --git a/llspy/cudabinwrapper.py b/llspy/cudabinwrapper.py index d8c85f2..b49d65a 100644 --- a/llspy/cudabinwrapper.py +++ b/llspy/cudabinwrapper.py @@ -349,7 +349,7 @@ def _get_options(self): def has_option(self, flag): """check the existence of a given flag in the binary help string.""" - return any([flag in key for key in self.options.keys()]) + return any(flag in key for key in self.options.keys()) def has_option_longname(self, name): """check the existence of a given flag in the binary help string.""" @@ -372,7 +372,7 @@ def describe_option(self, flag): print the description provided in the binary help string for a given flag """ if self.has_option(flag): - return self.options[[key for key in self.options.keys() if flag in key][0]] + return self.options[next(key for key in self.options.keys() if flag in key)] else: logger.warning(f'The flag "{flag}" is not listed in the help string.') diff --git a/llspy/gui/camcalibgui.py b/llspy/gui/camcalibgui.py index 1097da7..195e73e 100644 --- a/llspy/gui/camcalibgui.py +++ b/llspy/gui/camcalibgui.py @@ -43,7 +43,7 @@ def updatedarkstatus(prog): # first handle dark images darkavg = self.darkavg darkstd = self.darkstd - if not all([isinstance(a, np.ndarray) for a in (darkavg, darkstd)]): + if not all(isinstance(a, np.ndarray) for a in (darkavg, darkstd)): self.setStatus.emit("Loading dark images... [Step 1 of 4]") darklist = glob.glob(os.path.join(self.folder, "*dark*.tif")) numdark = len(darklist) @@ -139,7 +139,7 @@ def processFolder(self): elif os.path.isfile(os.path.join(folder, "dark_STD.tif")): darkstd = tf.imread(os.path.join(folder, "dark_STD.tif")) - if not all([isinstance(a, np.ndarray) for a in (darkavg, darkstd)]): + if not all(isinstance(a, np.ndarray) for a in (darkavg, darkstd)): if not pathHasPattern(folder, "*dark*.tif*"): QtW.QMessageBox.warning( self, diff --git a/llspy/gui/helpers.py b/llspy/gui/helpers.py index cffa637..3825c91 100644 --- a/llspy/gui/helpers.py +++ b/llspy/gui/helpers.py @@ -95,7 +95,7 @@ def string_to_iterable(string): it.extend(list(range(int(item[0]), int(item[1]) + 1, int(item[2])))) else: raise ValueError("Iterable string items must be of length <= 3") - return sorted(list(set(it))) + return sorted(set(it)) def guisave(widget, settings): diff --git a/llspy/gui/img_dialog.py b/llspy/gui/img_dialog.py index 473bf85..e51c46b 100644 --- a/llspy/gui/img_dialog.py +++ b/llspy/gui/img_dialog.py @@ -137,13 +137,13 @@ def setData(self, data): self.isComplex = data.dtype == np.complex64 or data.dtype == np.complex128 if self.ndim == 2: - self.shape = (1, 1, 1) + data.shape + self.shape = (1, 1, 1, *data.shape) self.data = data.copy().reshape(self.shape) elif self.ndim == 3: - self.shape = (1, 1) + data.shape + self.shape = (1, 1, *data.shape) self.data = data.copy().reshape(self.shape) elif self.ndim == 4: - self.shape = (1,) + data.shape + self.shape = (1, *data.shape) self.data = data.copy().reshape(self.shape) elif self.ndim == 5: self.shape = data.shape @@ -315,7 +315,7 @@ def f_c(x, y): def setDisplayOptions(self, options): self.displayOptions = options - if not ("cmap" in self.displayOptions): + if "cmap" not in self.displayOptions: self.displayOptions["cmap"] = "cubehelix" self.cmaps = tuple( {self.displayOptions["cmap"], "gray", "afmhot", "cubehelix", "inferno"} @@ -567,8 +567,8 @@ def update_axis_slider(self, axis, n): slid = getattr(self, axis.upper() + "slider") if n > 1: if not ( - (axis == "z" and getattr(self.data, "projection")) - or (axis == "c" and getattr(self.data, "_overlay")) + (axis == "z" and self.data.projection) + or (axis == "c" and self.data._overlay) ): widg.show() slid.setMaximum(n - 1) diff --git a/llspy/gui/mainwindow.py b/llspy/gui/mainwindow.py index 64316c6..ccaf6b1 100644 --- a/llspy/gui/mainwindow.py +++ b/llspy/gui/mainwindow.py @@ -79,7 +79,18 @@ class LLSDragDropTable(QtW.QTableWidget): - colHeaders = ["path", "name", "nC", "nT", "nZ", "nY", "nX", "angle", "dz", "dx"] + colHeaders = [ # noqa + "path", + "name", + "nC", + "nT", + "nZ", + "nY", + "nX", + "angle", + "dz", + "dx", + ] nCOLS = len(colHeaders) # A signal needs to be defined on class level: @@ -537,17 +548,12 @@ def loadRegistrationFile(self, file=None): try: with open(file) as json_data: regdict = json.load(json_data) - refs = sorted(list({t["reference"] for t in regdict["tforms"]})) + refs = sorted({t["reference"] for t in regdict["tforms"]}) # mov = set([t['moving'] for t in regdict['tforms']]) modes = ["None"] modes.extend( sorted( - list( - { - t["mode"].title().replace("Cpd", "CPD") - for t in regdict["tforms"] - } - ) + {t["mode"].title().replace("Cpd", "CPD") for t in regdict["tforms"]} ) ) self.RegCalib_channelRefCombo.clear() @@ -1692,7 +1698,7 @@ def undoRenameSelected(self): paths = self.listbox.renamedPaths for P in paths: - for root, subd, file in os.walk(P): + for root, subd, _file in os.walk(P): self.listbox.removePath(root) for d in subd: self.listbox.removePath(os.path.join(root, d)) @@ -1729,13 +1735,14 @@ def toggleOptOut(self, value): def checkBundled(self, value): if value: try: - bin = llspy.cudabinwrapper.get_bundled_binary() + bin_ = llspy.cudabinwrapper.get_bundled_binary() except llspy.cudabinwrapper.CUDAbinException: logger.warning( - "Could not load bundled cudaDeconv. Check that it is installed. read docs" + "Could not load bundled cudaDeconv. " + "Check that it is installed read docs" ) return - self.setBinaryPath(bin) + self.setBinaryPath(bin_) else: self.setBinaryPath(self.cudaDeconvPathLineEdit.text()) @@ -1831,12 +1838,10 @@ def showAboutWindow(self): QtW.QMessageBox.about( self, "LLSpy", - """LLSpy v.{}\n -Copyright © {}, President and Fellows of Harvard College. All rights reserved.\n\n + f"""LLSpy v.{llspy.__version__}\n +Copyright © {now.year}, President and Fellows of Harvard College. All rights reserved.\n\n Developed by Talley Lambert\n\n -The cudaDeconv deconvolution program was written by Lin Shao and by Dan Milkie at Janelia Research Campus, and modified by Talley Lambert for LLSpy. """.format( - llspy.__version__, now.year - ), +The cudaDeconv deconvolution program was written by Lin Shao and by Dan Milkie at Janelia Research Campus, and modified by Talley Lambert for LLSpy. """, ) def showHelpWindow(self): diff --git a/llspy/gui/qtlogger.py b/llspy/gui/qtlogger.py index b2d3123..82274b2 100644 --- a/llspy/gui/qtlogger.py +++ b/llspy/gui/qtlogger.py @@ -57,6 +57,6 @@ def __init__(self, **kwargs): def filter(self, record): permitted = ["root", "llspy", "spimagine", "fiducialreg", "gputools"] - if any(record.name.startswith(l) for l in permitted): + if any(record.name.startswith(x) for x in permitted): return True return False diff --git a/llspy/gui/workers.py b/llspy/gui/workers.py index 42a9042..03ea0e9 100644 --- a/llspy/gui/workers.py +++ b/llspy/gui/workers.py @@ -110,9 +110,7 @@ def procErrorRead(self): def onFinished(self, exitCode, exitStatus): statusmsg = {0: "exited normally", 1: "crashed"} self._logger.info( - "{} #{} {} with exit code: {}".format( - self.name, self.id, statusmsg[exitStatus], exitCode - ) + f"{self.name} #{self.id} {statusmsg[exitStatus]} with exit code: {exitCode}" ) self.finished.emit() @@ -146,9 +144,7 @@ def procReadyRead(self): def onFinished(self, exitCode, exitStatus): statusmsg = {0: "exited normally", 1: "crashed"} self._logger.info( - "{} #{} {} with exit code: {}".format( - self.name, self.id, statusmsg[exitStatus], exitCode - ) + f"{self.name} #{self.id} {statusmsg[exitStatus]} with exit code: {exitCode}" ) self.finished.emit(self.id) @@ -203,7 +199,7 @@ def work(self): if llspy.util.find_filepattern(self.path, "*.tar*"): raise err.LLSpyError( "There are both raw tiffs and a compressed file in " - "directory: {}".format(self.path), + f"directory: {self.path}", "If you would like to compress this directory, " "please either remove any existing *.tar files, or remove " "the uncompressed tiff files. Alternatively, you can use " @@ -422,7 +418,7 @@ def work(self): raise # if not flash correcting but there is trimming/median filter requested elif self.P.medianFilter or any( - [any(i) for i in (self.P.trimX, self.P.trimY, self.P.trimZ)] + any(i) for i in (self.P.trimX, self.P.trimY, self.P.trimZ) ): self.E.path = self.E.median_and_trim(**self.P) @@ -504,9 +500,7 @@ def on_file_finished(self): # update status bar self.nFiles_done = self.nFiles_done + 1 self.status_update.emit( - "Processing {}: ({} of {})".format( - self.shortname, self.nFiles_done, self.nFiles - ) + f"Processing {self.shortname}: ({self.nFiles_done} of {self.nFiles})" ) # update progress bar self.progressUp.emit() @@ -530,7 +524,7 @@ def on_CUDAworker_done(self, worker_id): # ... only as fast as the slowest GPU # could probably fix easily by delivering a value to startCudaWorkers # instead of looping through gpu inside of it - if not any([v for v in self.__CUDAthreads.values()]): + if not any(v for v in self.__CUDAthreads.values()): # if there's still stuff left in the argQueue for this item, keep going if self.aborted: self.aborted = False @@ -596,7 +590,7 @@ def post_process(self): @QtCore.Slot() def abort(self): self._logger.info(f"LLSworker #{self.__id} notified to abort") - if any([v for v in self.__CUDAthreads.values()]): + if any(v for v in self.__CUDAthreads.values()): self.aborted = True self.__argQueue = [] self.sig_abort.emit() diff --git a/llspy/libcudawrapper.py b/llspy/libcudawrapper.py index 528fd7d..6da348d 100644 --- a/llspy/libcudawrapper.py +++ b/llspy/libcudawrapper.py @@ -216,7 +216,7 @@ def affineGPU(im, tmat, dzyx=None): result = np.empty((nz, ny, nx), dtype=np.float32) if ( isinstance(dzyx, (tuple, list)) - and all([isinstance(i, float) for i in dzyx]) + and all(isinstance(i, float) for i in dzyx) and len(dzyx) == 3 ): # note, dzyx coordinate order is flipped when handing to Affine_interface_RA diff --git a/llspy/libinstall.py b/llspy/libinstall.py index f0dad8e..17b7786 100644 --- a/llspy/libinstall.py +++ b/llspy/libinstall.py @@ -57,7 +57,7 @@ def find_libpath(path): if is_libpath(os.path.join(path, "lib")): return os.path.join(path, "lib") else: - for dirpath, dirnames, filenames in os.walk(path): + for _dirpath, dirnames, _filenames in os.walk(path): for P in dirnames: if is_libpath(P): return P @@ -77,7 +77,7 @@ def find_binpath(path): if is_binpath(os.path.join(path, "bin")): return os.path.join(path, "bin") else: - for dirpath, dirnames, filenames in os.walk(path): + for _dirpath, dirnames, _filenames in os.walk(path): for P in dirnames: if is_binpath(P): return P @@ -156,9 +156,7 @@ def install(dirpath, dryrun=False): os.remove(D) except PermissionError: print( - "Permission Error: you must manually remove or replace this file: {}".format( - D - ) + f"Permission Error: you must manually remove or replace this file: {D}" ) continue copyfile(src, D) diff --git a/llspy/llsdir.py b/llspy/llsdir.py index 5bf5973..188b1d3 100644 --- a/llspy/llsdir.py +++ b/llspy/llsdir.py @@ -13,13 +13,12 @@ import numpy as np import tifffile as tf +from parse import parse as _parse from llspy.libcudawrapper import affineGPU, deskewGPU, quickDecon -from parse import parse as _parse -from . import arrayfun, compress, config +from . import arrayfun, compress, config, parse, schema, util from . import otf as otfmodule -from . import parse, schema, util from .camera import CameraParameters, selectiveMedianFilter from .cudabinwrapper import CUDAbin from .exceptions import LLSpyError, OTFError @@ -119,7 +118,7 @@ def filter_stack(filename, outname, dx, background, trim, medianFilter): stack = util.imread(filename) if medianFilter: stack, _ = selectiveMedianFilter(stack, background) - if any([any(i) for i in trim]): + if any(any(i) for i in trim): stack = arrayfun.trimedges(stack, trim) util.imsave(util.reorderstack(np.squeeze(stack), "zyx"), outname, dx=dx, dz=1) @@ -170,14 +169,14 @@ def get_regObj(regCalibPath): return refObj -def register_folder( - folder, regRefWave, regMode, regObj, voxsize=[1, 1, 1], discard=False -): +def register_folder(folder, regRefWave, regMode, regObj, voxsize=None, discard=False): """Register all (non-reference) wavelengths in a folder to the specified reference wavelength, using the provided regObj. voxsize must be an array of pixel sizes [dz, dy, dx] """ + if voxsize is None: + voxsize = [1, 1, 1] if isinstance(regObj, str): regObj = get_regObj(regObj) folder = str(folder) @@ -266,7 +265,7 @@ def preview(exp, tR=0, cR=None, **kwargs): if not isinstance(exp, LLSdir): if isinstance(exp, str): exp = LLSdir(exp) - logger.debug(f"Preview called on {str(exp.path)}") + logger.debug(f"Preview called on {exp.path!s}") logger.debug(f"Params: {exp.parameters}") if exp.is_compressed(): @@ -308,7 +307,7 @@ def preview(exp, tR=0, cR=None, **kwargs): else: # camera correction trims edges, so if we aren't doing the camera correction # we need to call the edge trim on our own - if any([any(i) for i in (P.trimZ, P.trimY, P.trimX)]): + if any(any(i) for i in (P.trimZ, P.trimY, P.trimX)): stacks = [ arrayfun.trimedges(s, (P.trimZ, P.trimY, P.trimX)) for s in stacks ] @@ -367,8 +366,7 @@ def preview(exp, tR=0, cR=None, **kwargs): ) else: logger.error( - "Registration Calibration dir not valid" - "{}".format(P.regCalibPath) + "Registration Calibration dir not valid" f"{P.regCalibPath}" ) out.append(np.stack(stacks, 0)) @@ -399,7 +397,7 @@ def process(exp, binary=None, **kwargs): if not isinstance(exp, LLSdir): if isinstance(exp, str): exp = LLSdir(exp) - logger.debug(f"Process called on {str(exp.path)}") + logger.debug(f"Process called on {exp.path!s}") logger.debug(f"Params: {exp.parameters}") if exp.is_compressed(): @@ -419,7 +417,7 @@ def process(exp, binary=None, **kwargs): if P.correctFlash: exp.path = exp.correct_flash(**P) - elif P.medianFilter or any([any(i) for i in (P.trimX, P.trimY, P.trimZ)]): + elif P.medianFilter or any(any(i) for i in (P.trimX, P.trimY, P.trimZ)): exp.path = exp.median_and_trim(**P) if P.nIters > 0 or P.saveDeskewedRaw or P.rotate: @@ -449,9 +447,7 @@ def process(exp, binary=None, **kwargs): ): # processing all the timepoints filepattern = f"ch{chan}_" else: - filepattern = "ch{}_stack{}".format( - chan, util.pyrange_to_perlregex(P.tRange) - ) + filepattern = f"ch{chan}_stack{util.pyrange_to_perlregex(P.tRange)}" binary.process(str(exp.path), filepattern, P.otfs[chan], **opts) @@ -499,7 +495,7 @@ def mergemips(folder, axis, write=True, dx=1, dt=1, delete=True, fpattern=None): fpattern = __FPATTERN__ folder = plib.Path(folder) if not folder.is_dir(): - raise OSError(f"MIP folder does not exist: {str(folder)}") + raise OSError(f"MIP folder does not exist: {folder!s}") try: filelist = [] @@ -556,7 +552,7 @@ def mergemips(folder, axis, write=True, dx=1, dt=1, delete=True, fpattern=None): return stack except ValueError as e: - logger.error(f"ERROR: failed to merge MIPs from {str(folder)}: ") + logger.error(f"ERROR: failed to merge MIPs from {folder!s}: ") logger.error(f"{e}") @@ -840,12 +836,10 @@ def read_tiff_header(self): with tf.TiffFile(self.tiff.raw[0]) as firstTiff: self.parameters.shape = firstTiff.series[0].shape try: - self.tiff.bit_depth = getattr(firstTiff.pages[0], "bitspersample") + self.tiff.bit_depth = firstTiff.pages[0].bitspersample except AttributeError: try: - self.tiff.bit_depth = getattr( - firstTiff.pages[0], "bits_per_sample" - ) + self.tiff.bit_depth = firstTiff.pages[0].bits_per_sample except AttributeError: self.tiff.bit_depth = 16 ( @@ -924,9 +918,7 @@ def reduce_to_raw(self, keepmip=True, verbose=True): shutil.rmtree(str(self.path.joinpath(folder))) except Exception as e: logger.error( - "unable to remove directory: {}".format( - self.path.joinpath(folder) - ) + f"unable to remove directory: {self.path.joinpath(folder)}" ) logger.error(e) return 0 @@ -977,7 +969,7 @@ def localParams(self, recalc=False, **kwargs): """ # allow for 'lazy' storage of previously calculated value if "_localParams" in dir(self) and not recalc: - if all([self._localParams[k] == v for k, v in kwargs.items()]): + if all(self._localParams[k] == v for k, v in kwargs.items()): return self._localParams _schema = schema.procParams(kwargs) assert ( @@ -1021,15 +1013,11 @@ def localParams(self, recalc=False, **kwargs): _schema.tRange = [minT] if max(list(_schema.tRange)) > maxT: logger.warning( - "max tRange was greater than the last timepoint. Excluding T > {}".format( - maxT - ) + f"max tRange was greater than the last timepoint. Excluding T > {maxT}" ) if min(list(_schema.tRange)) < minT: logger.warning( - "min tRange was less than the first timepoint. Excluding < {}".format( - minT - ) + f"min tRange was less than the first timepoint. Excluding < {minT}" ) assert len(_schema.tRange), "No valid timepoints!" @@ -1180,14 +1168,11 @@ def get_otf(self, wave, otfpath=config.__OTFPATH__): if mask: raise OTFError( "Could not find OTF for " - "wave {}, mask {}-{} in path: {}".format( - wave, outerNA, innerNA, otfpath - ) + f"wave {wave}, mask {outerNA}-{innerNA} in path: {otfpath}" ) else: raise OTFError( - "Could not find OTF for " - "wave {} in path: {}".format(wave, otfpath) + "Could not find OTF for " f"wave {wave} in path: {otfpath}" ) return otf @@ -1363,9 +1348,7 @@ def register(self, regRefWave, regMode, regCalibPath, discard=False): D, regRefWave, regMode, regObj, voxsize, discard=discard ) else: - logger.error( - "Registration Calibration path not valid" "{}".format(regCalibPath) - ) + logger.error("Registration Calibration path not valid" f"{regCalibPath}") def toJSON(self): import json @@ -1427,7 +1410,7 @@ def getdata(self): return [util.imread(f) for f in self.get_t(self.t)] def has_data(self): - return all([isinstance(a, np.ndarray) for a in self.data]) + return all(isinstance(a, np.ndarray) for a in self.data) def toJSON(self): D = self.__dict__.copy() @@ -1502,7 +1485,7 @@ def default(self, obj): if isinstance(obj, np.ndarray): if all(isinstance(i, np.ndarray) for i in obj): nestedList = obj.tolist() - result = [self.fixedString(l) for l in nestedList] + result = [self.fixedString(x) for x in nestedList] return result else: return obj.tolist() diff --git a/llspy/otf.py b/llspy/otf.py index f2dba93..8be089d 100644 --- a/llspy/otf.py +++ b/llspy/otf.py @@ -126,10 +126,8 @@ def makeotf( def dir_has_otfs(dirname): if os.path.isdir(str(dirname)): if any( - [ - (psffile_pattern.search(t) or default_otf_pattern.search(t)) - for t in os.listdir(dirname) - ] + (psffile_pattern.search(t) or default_otf_pattern.search(t)) + for t in os.listdir(dirname) ): return True return False diff --git a/llspy/samples.py b/llspy/samples.py index 7c6e5e2..1bce0c9 100644 --- a/llspy/samples.py +++ b/llspy/samples.py @@ -17,19 +17,13 @@ filename = "cell5_ch0_stack0000_488nm_0000000msec_0020931273msecAbs.tif" -reg = dict( - { - "ex1": dict( - { - "tspeck": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex1/tspeck/", - "data": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex1/data/", - } - ), - "ex2": dict( - { - "tspeck": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex2/tspeck/", - "data": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex2/data/", - } - ), - } -) +reg = { + "ex1": { + "tspeck": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex1/tspeck/", + "data": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex1/data/", + }, + "ex2": { + "tspeck": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex2/tspeck/", + "data": "/Users/talley/DropboxHMS/CBMF/lattice_sample_data/lls_registration_samp/reg_ex2/data/", + }, +} diff --git a/llspy/schema.py b/llspy/schema.py index 7a07860..4a229c5 100644 --- a/llspy/schema.py +++ b/llspy/schema.py @@ -28,7 +28,7 @@ def CTiterable(v): iter(v) except TypeError: raise TypeError("Not an iterable object") - if not all([(isinstance(i, int) and i >= 0) for i in v]): + if not all((isinstance(i, int) and i >= 0) for i in v): raise ValueError("All values in Channel/Time range must be integers >= 0") return v @@ -339,7 +339,7 @@ def validateItems(**kwargs): if k not in __validator__: print(f"ERROR! got unrecognized key: {k}") return 0 - S = Schema({k: v for k, v in __validator__.items()}, extra=PREVENT_EXTRA) + S = Schema(dict(__validator__.items()), extra=PREVENT_EXTRA) return validate_with_humanized_errors(kwargs, S) diff --git a/llspy/util.py b/llspy/util.py index 6b612bb..559c47a 100644 --- a/llspy/util.py +++ b/llspy/util.py @@ -88,7 +88,7 @@ def imsave(arr, outpath, dx=1, dz=1, dt=1, unit="micron"): def getfoldersize(folder, recurse=False): if recurse: total_size = 0 - for dirpath, dirnames, filenames in os.walk(folder): + for dirpath, _dirnames, filenames in os.walk(folder): for f in filenames: total_size += os.path.getsize(os.path.join(dirpath, f)) return total_size @@ -168,15 +168,17 @@ def walklevel(some_dir, level=1): def get_subfolders_containing_filepattern( - dirname, filepattern="*Settings.txt", exclude=["Corrected"], level=1 + dirname, filepattern="*Settings.txt", exclude=None, level=1 ): """retrieve a list of subdirectories of the input directory that contain a filepattern... useful for getting raw data directories for batch processing """ + if exclude is None: + exclude = ["Corrected"] matches = [] - for root, dirnames, filenames in walklevel(dirname, level): - for filename in fnmatch.filter(filenames, filepattern): - if not any([e in root for e in exclude]): + for root, _dirnames, filenames in walklevel(dirname, level): + for _filename in fnmatch.filter(filenames, filepattern): + if not any(e in root for e in exclude): matches.append(root) return matches diff --git a/pyproject.toml b/pyproject.toml index 06204c3..3be7ceb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,29 @@ write_to = "llspy/_version.py" [tool.check-manifest] ignore = ["llspy/_version.py", ".pre-commit-config.yaml"] + +# https://docs.astral.sh/ruff +[tool.ruff] +line-length = 88 +target-version = "py37" +src = ["src"] +# https://docs.astral.sh/ruff/rules +select = [ + "E", # style errors + "W", # style warnings + "F", # flakes + "I", # isort + "UP", # pyupgrade + "C4", # flake8-comprehensions + # "B", # flake8-bugbear + "A001", # flake8-builtins + "RUF", # ruff-specific rules + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports +] +ignore = [ + "E501", # line length +] + + +exclude = ["docs/*"] diff --git a/setup.cfg b/setup.cfg index 074a69d..a5ef166 100644 --- a/setup.cfg +++ b/setup.cfg @@ -71,13 +71,3 @@ test = [bdist_wheel] universal = 1 - -[flake8] -exclude = docs,_version.py,.eggs,conf.py -max-line-length = 88 -docstring-convention = numpy -ignore = D100, D213, D401, D413, D107, W503, E501, E203, E741 - -[isort] -profile = black -src_paths = llspy diff --git a/tests/conftest.py b/tests/conftest.py index 3b5189b..ed297d2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ import pytest - from llspy.libcudawrapper import cudaLib requires_cuda = pytest.mark.skipif(not cudaLib, reason="Cannot test without CUDA") diff --git a/tests/test_gui.py b/tests/test_gui.py index 851d1ac..d2f4f2b 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -2,7 +2,6 @@ from pathlib import Path import pytest - from llspy.exceptions import CUDAbinException from llspy.gui.exceptions import MissingBinaryError from llspy.gui.mainwindow import main_GUI diff --git a/tests/test_llsdir.py b/tests/test_llsdir.py index 7141ca4..10720f1 100644 --- a/tests/test_llsdir.py +++ b/tests/test_llsdir.py @@ -20,9 +20,8 @@ def hash_dir(dir_path): files ): # we sort to guarantee that files will always go in the same order hashes.append(sha1OfFile(os.path.join(path, file))) - for dir in sorted( - dirs - ): # we sort to guarantee that dirs will always go in the same order - hashes.append(hash_dir(os.path.join(path, dir))) + # we sort to guarantee that dirs will always go in the same order + for _dir in sorted(dirs): + hashes.append(hash_dir(os.path.join(path, _dir))) break # we only need one iteration - to get files and dirs in current directory return str(hash("".join(hashes))) diff --git a/tests/test_processing.py b/tests/test_processing.py index 6b80aac..6009988 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -1,9 +1,8 @@ import os -from qtpy import QtCore - from llspy.gui.mainwindow import main_GUI from llspy.llsdir import LLSdir +from qtpy import QtCore from .conftest import requires_cuda From 9467849e7b7752a4ed95111e2119864812e128ec Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 10:43:07 -0500 Subject: [PATCH 04/24] update ci --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36c7ea1..a044efc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,13 @@ jobs: mamba-version: "*" channels: conda-forge,defaults channel-priority: true + + - name: Install OpenCL + if: matrix.platform == 'ubuntu-latest' + run: mamba install pyopencl pocl - name: Install dependencies run: | - mamba install pyopencl pocl pip install -U pip pip install -e .[test] @@ -42,6 +45,7 @@ jobs: uses: aganders3/headless-gui@v2 with: run: pytest --cov llspy + shell: bash -l {0} - name: Coverage uses: codecov/codecov-action@v3 From 4d1ba656e94355f4a1479d48146455f05cb41469 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Dec 2023 15:43:20 +0000 Subject: [PATCH 05/24] style(pre-commit.ci): auto fixes [...] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a044efc..819e514 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: mamba-version: "*" channels: conda-forge,defaults channel-priority: true - + - name: Install OpenCL if: matrix.platform == 'ubuntu-latest' run: mamba install pyopencl pocl From 71ef47dd0c10b2d1c33bb3770510ad57f7549441 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 10:53:48 -0500 Subject: [PATCH 06/24] remove setup --- pyproject.toml | 75 ++++++++++++++++++++++++++++++++++++++++++++++---- setup.cfg | 73 ------------------------------------------------ 2 files changed, 70 insertions(+), 78 deletions(-) delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 3be7ceb..092e194 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,8 +5,73 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] write_to = "llspy/_version.py" -[tool.check-manifest] -ignore = ["llspy/_version.py", ".pre-commit-config.yaml"] +# https://peps.python.org/pep-0621/ +[project] +name = "llspy" +dynamic = ["version"] +description = "Lattice Light Sheet Processing Tools." +readme = "README.rst" +requires-python = ">=3.7" +license = { text = "BSD-3-Clause" } +authors = [{ name = "Talley Lambert", email = "talley.lambert@example.com" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Visualization", +] +dependencies = [ + "click", + "llspy-slm>=0.2.1", + "matplotlib", + "numpy", + "parse", + "qtpy", + "scipy", + "sentry-sdk", + "tifffile", + "voluptuous", + "watchdog", + "numba;python_version < '3.12'", +] + +# https://peps.python.org/pep-0621/#dependencies-optional-dependencies +[project.optional-dependencies] +napari = ["napari"] +pyqt = ["PyQt5"] +pyside = ["PySide2"] +spimagine = ["spimagine"] +test = ["pytest", "pytest-cov", "pytest-qt"] +dev = [ + "black", + "ipython", + "mypy", + "pdbpp", # https://github.com/pdbpp/pdbpp + "pre-commit", + "rich", # https://github.com/Textualize/rich + "ruff", +] + +[project.urls] +homepage = "https://github.com/tlambert03/LLSpy" +repository = "https://github.com/tlambert03/LLSpy" + +[project.scripts] +lls = "llspy.bin.llspy_cli:cli" +lls-gui = "llspy.bin.llspy_gui:main" + +[tool.setuptools.packages.find] +exclude = ["tests", "docs", "pyinstaller"] # https://docs.astral.sh/ruff [tool.ruff] @@ -21,7 +86,6 @@ select = [ "I", # isort "UP", # pyupgrade "C4", # flake8-comprehensions - # "B", # flake8-bugbear "A001", # flake8-builtins "RUF", # ruff-specific rules "TCH", # flake8-type-checking @@ -30,6 +94,7 @@ select = [ ignore = [ "E501", # line length ] - - exclude = ["docs/*"] + +[tool.check-manifest] +ignore = ["llspy/_version.py", ".pre-commit-config.yaml"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a5ef166..0000000 --- a/setup.cfg +++ /dev/null @@ -1,73 +0,0 @@ -[metadata] -name = llspy -description = Lattice Light Sheet Processing Tools -long_description = file: README.rst -long_description_content_type = text/x-rst -url = https://github.com/tlambert03/LLSpy -author = Talley Lambert -author_email = talley.lambert@gmail.com -license = BSD 3-clause -license_files = LICENSE -classifiers = - Development Status :: 3 - Alpha - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Natural Language :: English - Operating System :: Microsoft :: Windows - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Topic :: Scientific/Engineering - Topic :: Scientific/Engineering :: Visualization - -[options] -packages = find: -install_requires = - click - llspy-slm>=0.2.1 - matplotlib - numpy - parse - qtpy - scipy - sentry-sdk - tifffile - voluptuous - watchdog - numba;python_version < '3.12' -python_requires = >=3.8 -include_package_data = True -zip_safe = False - -[options.packages.find] -exclude = - tests - docs - pyinstaller - -[options.entry_points] -console_scripts = - lls = llspy.bin.llspy_cli:cli - lls-gui = llspy.bin.llspy_gui:main - -[options.extras_require] -napari = - napari -pyqt = - PyQt5 -pyside = - PySide2 -spimagine = - spimagine -test = - PyQt5 - pytest - pytest-cov - pytest-qt - -[options.package_data] -* = *.ini, *.ui, *.png - -[bdist_wheel] -universal = 1 From cab9515f8e5ed91f366e2f3d7c457cf180922e07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Dec 2023 15:54:02 +0000 Subject: [PATCH 07/24] style(pre-commit.ci): auto fixes [...] --- llspy/libcudawrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llspy/libcudawrapper.py b/llspy/libcudawrapper.py index 6da348d..3b55591 100644 --- a/llspy/libcudawrapper.py +++ b/llspy/libcudawrapper.py @@ -301,7 +301,7 @@ def RL_init( deskew=31.5, rotate=0, width=0, - **kwargs + **kwargs, ): requireCUDAlib() nz, ny, nx = rawdata_shape From f75d0c659d2945e795837a72210bb1090c949db9 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 10:55:48 -0500 Subject: [PATCH 08/24] add check manifest test --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 819e514..bf1229f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,12 @@ on: workflow_dispatch: jobs: + check-manifest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: pipx run check-manifest + test: name: ${{ matrix.platform }} (${{ matrix.python-version }}) runs-on: ${{ matrix.platform }} From f8b1c4ffe074559128b34b13d6e37365a2fe5844 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 11:11:26 -0500 Subject: [PATCH 09/24] src layout --- README.rst | 10 ++-------- pyproject.toml | 4 ++-- {fiducialreg => src/fiducialreg}/__init__.py | 0 {fiducialreg => src/fiducialreg}/fiducialreg.py | 0 {fiducialreg => src/fiducialreg}/imref.py | 0 {fiducialreg => src/fiducialreg}/imwarp.py | 0 {llspy => src/llspy}/__init__.py | 0 {llspy => src/llspy}/__main__.py | 0 src/llspy/_version.py | 16 ++++++++++++++++ {llspy => src/llspy}/arrayfun.py | 0 {llspy => src/llspy}/bin/__init__.py | 0 {llspy => src/llspy}/bin/llspy_cli.py | 0 {llspy => src/llspy}/bin/llspy_gui.py | 0 {llspy => src/llspy}/camcalib.py | 0 {llspy => src/llspy}/camera.py | 0 {llspy => src/llspy}/compress.py | 0 {llspy => src/llspy}/config.py | 0 {llspy => src/llspy}/cudabinwrapper.py | 0 {llspy => src/llspy}/exceptions.py | 0 {llspy => src/llspy}/gui/__init__.py | 0 {llspy => src/llspy}/gui/before_after.png | Bin {llspy => src/llspy}/gui/camcalibgui.py | 0 {llspy => src/llspy}/gui/camcordialog.py | 0 {llspy => src/llspy}/gui/camcordialog.ui | 0 {llspy => src/llspy}/gui/exceptions.py | 0 {llspy => src/llspy}/gui/guiDefaults.ini | 0 {llspy => src/llspy}/gui/helpers.py | 0 {llspy => src/llspy}/gui/img_dialog.py | 0 {llspy => src/llspy}/gui/img_window.py | 0 {llspy => src/llspy}/gui/img_window.ui | 0 {llspy => src/llspy}/gui/logo_dark.png | Bin {llspy => src/llspy}/gui/logo_light.png | Bin {llspy => src/llspy}/gui/main_gui.py | 0 {llspy => src/llspy}/gui/main_gui.ui | 0 {llspy => src/llspy}/gui/mainwindow.py | 0 {llspy => src/llspy}/gui/qtlogger.py | 0 {llspy => src/llspy}/gui/styles.py | 0 {llspy => src/llspy}/gui/watcher.py | 0 {llspy => src/llspy}/gui/workers.py | 0 {llspy => src/llspy}/libcudawrapper.py | 0 {llspy => src/llspy}/libinstall.py | 0 {llspy => src/llspy}/llsdir.py | 0 {llspy => src/llspy}/otf.py | 0 {llspy => src/llspy}/parse.py | 0 {llspy => src/llspy}/register_old.py | 0 {llspy => src/llspy}/samples.py | 0 {llspy => src/llspy}/schema.py | 0 {llspy => src/llspy}/settingstxt.py | 0 {llspy => src/llspy}/util.py | 0 {llspy => src/llspy}/xzpsf.py | 0 50 files changed, 20 insertions(+), 10 deletions(-) rename {fiducialreg => src/fiducialreg}/__init__.py (100%) rename {fiducialreg => src/fiducialreg}/fiducialreg.py (100%) rename {fiducialreg => src/fiducialreg}/imref.py (100%) rename {fiducialreg => src/fiducialreg}/imwarp.py (100%) rename {llspy => src/llspy}/__init__.py (100%) rename {llspy => src/llspy}/__main__.py (100%) create mode 100644 src/llspy/_version.py rename {llspy => src/llspy}/arrayfun.py (100%) rename {llspy => src/llspy}/bin/__init__.py (100%) rename {llspy => src/llspy}/bin/llspy_cli.py (100%) rename {llspy => src/llspy}/bin/llspy_gui.py (100%) rename {llspy => src/llspy}/camcalib.py (100%) rename {llspy => src/llspy}/camera.py (100%) rename {llspy => src/llspy}/compress.py (100%) rename {llspy => src/llspy}/config.py (100%) rename {llspy => src/llspy}/cudabinwrapper.py (100%) rename {llspy => src/llspy}/exceptions.py (100%) rename {llspy => src/llspy}/gui/__init__.py (100%) rename {llspy => src/llspy}/gui/before_after.png (100%) rename {llspy => src/llspy}/gui/camcalibgui.py (100%) rename {llspy => src/llspy}/gui/camcordialog.py (100%) rename {llspy => src/llspy}/gui/camcordialog.ui (100%) rename {llspy => src/llspy}/gui/exceptions.py (100%) rename {llspy => src/llspy}/gui/guiDefaults.ini (100%) rename {llspy => src/llspy}/gui/helpers.py (100%) rename {llspy => src/llspy}/gui/img_dialog.py (100%) rename {llspy => src/llspy}/gui/img_window.py (100%) rename {llspy => src/llspy}/gui/img_window.ui (100%) rename {llspy => src/llspy}/gui/logo_dark.png (100%) rename {llspy => src/llspy}/gui/logo_light.png (100%) rename {llspy => src/llspy}/gui/main_gui.py (100%) rename {llspy => src/llspy}/gui/main_gui.ui (100%) rename {llspy => src/llspy}/gui/mainwindow.py (100%) rename {llspy => src/llspy}/gui/qtlogger.py (100%) rename {llspy => src/llspy}/gui/styles.py (100%) rename {llspy => src/llspy}/gui/watcher.py (100%) rename {llspy => src/llspy}/gui/workers.py (100%) rename {llspy => src/llspy}/libcudawrapper.py (100%) rename {llspy => src/llspy}/libinstall.py (100%) rename {llspy => src/llspy}/llsdir.py (100%) rename {llspy => src/llspy}/otf.py (100%) rename {llspy => src/llspy}/parse.py (100%) rename {llspy => src/llspy}/register_old.py (100%) rename {llspy => src/llspy}/samples.py (100%) rename {llspy => src/llspy}/schema.py (100%) rename {llspy => src/llspy}/settingstxt.py (100%) rename {llspy => src/llspy}/util.py (100%) rename {llspy => src/llspy}/xzpsf.py (100%) diff --git a/README.rst b/README.rst index 7b68c7d..ce993c1 100755 --- a/README.rst +++ b/README.rst @@ -175,18 +175,12 @@ Installation #. Install `conda `_ #. Launch a ``terminal`` window (Linux), or ``Anaconda Prompt`` (Windows) -#. Add the "conda-forge" and "talley" channels to your conda config - - .. code:: bash - - $ conda config --add channels conda-forge - $ conda config --add channels talley - #. Install LLSpy into a new conda environment .. code:: bash - $ conda create -n llsenv python=3.11 llspy + # `-c talley` is required to install the llspylibs package + $ conda create -n llsenv -c talley python=3.11 llspylibs $ conda activate llsenv The ``create -n llsenv`` line creates a virtual environment. This is optional, but recommended as it easier to uninstall cleanly and prevents conflicts with any other python environments. If installing into a virtual environment, you must source the environment before proceeding, and each time before using llspy. diff --git a/pyproject.toml b/pyproject.toml index 092e194..09ef226 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] -write_to = "llspy/_version.py" +write_to = "src/llspy/_version.py" # https://peps.python.org/pep-0621/ [project] @@ -71,7 +71,7 @@ lls = "llspy.bin.llspy_cli:cli" lls-gui = "llspy.bin.llspy_gui:main" [tool.setuptools.packages.find] -exclude = ["tests", "docs", "pyinstaller"] +where = ["src"] # https://docs.astral.sh/ruff [tool.ruff] diff --git a/fiducialreg/__init__.py b/src/fiducialreg/__init__.py similarity index 100% rename from fiducialreg/__init__.py rename to src/fiducialreg/__init__.py diff --git a/fiducialreg/fiducialreg.py b/src/fiducialreg/fiducialreg.py similarity index 100% rename from fiducialreg/fiducialreg.py rename to src/fiducialreg/fiducialreg.py diff --git a/fiducialreg/imref.py b/src/fiducialreg/imref.py similarity index 100% rename from fiducialreg/imref.py rename to src/fiducialreg/imref.py diff --git a/fiducialreg/imwarp.py b/src/fiducialreg/imwarp.py similarity index 100% rename from fiducialreg/imwarp.py rename to src/fiducialreg/imwarp.py diff --git a/llspy/__init__.py b/src/llspy/__init__.py similarity index 100% rename from llspy/__init__.py rename to src/llspy/__init__.py diff --git a/llspy/__main__.py b/src/llspy/__main__.py similarity index 100% rename from llspy/__main__.py rename to src/llspy/__main__.py diff --git a/src/llspy/_version.py b/src/llspy/_version.py new file mode 100644 index 0000000..41da9aa --- /dev/null +++ b/src/llspy/_version.py @@ -0,0 +1,16 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = '0.5.1.dev13+gb51b1a7.d20231209' +__version_tuple__ = version_tuple = (0, 5, 1, 'dev13', 'gb51b1a7.d20231209') diff --git a/llspy/arrayfun.py b/src/llspy/arrayfun.py similarity index 100% rename from llspy/arrayfun.py rename to src/llspy/arrayfun.py diff --git a/llspy/bin/__init__.py b/src/llspy/bin/__init__.py similarity index 100% rename from llspy/bin/__init__.py rename to src/llspy/bin/__init__.py diff --git a/llspy/bin/llspy_cli.py b/src/llspy/bin/llspy_cli.py similarity index 100% rename from llspy/bin/llspy_cli.py rename to src/llspy/bin/llspy_cli.py diff --git a/llspy/bin/llspy_gui.py b/src/llspy/bin/llspy_gui.py similarity index 100% rename from llspy/bin/llspy_gui.py rename to src/llspy/bin/llspy_gui.py diff --git a/llspy/camcalib.py b/src/llspy/camcalib.py similarity index 100% rename from llspy/camcalib.py rename to src/llspy/camcalib.py diff --git a/llspy/camera.py b/src/llspy/camera.py similarity index 100% rename from llspy/camera.py rename to src/llspy/camera.py diff --git a/llspy/compress.py b/src/llspy/compress.py similarity index 100% rename from llspy/compress.py rename to src/llspy/compress.py diff --git a/llspy/config.py b/src/llspy/config.py similarity index 100% rename from llspy/config.py rename to src/llspy/config.py diff --git a/llspy/cudabinwrapper.py b/src/llspy/cudabinwrapper.py similarity index 100% rename from llspy/cudabinwrapper.py rename to src/llspy/cudabinwrapper.py diff --git a/llspy/exceptions.py b/src/llspy/exceptions.py similarity index 100% rename from llspy/exceptions.py rename to src/llspy/exceptions.py diff --git a/llspy/gui/__init__.py b/src/llspy/gui/__init__.py similarity index 100% rename from llspy/gui/__init__.py rename to src/llspy/gui/__init__.py diff --git a/llspy/gui/before_after.png b/src/llspy/gui/before_after.png similarity index 100% rename from llspy/gui/before_after.png rename to src/llspy/gui/before_after.png diff --git a/llspy/gui/camcalibgui.py b/src/llspy/gui/camcalibgui.py similarity index 100% rename from llspy/gui/camcalibgui.py rename to src/llspy/gui/camcalibgui.py diff --git a/llspy/gui/camcordialog.py b/src/llspy/gui/camcordialog.py similarity index 100% rename from llspy/gui/camcordialog.py rename to src/llspy/gui/camcordialog.py diff --git a/llspy/gui/camcordialog.ui b/src/llspy/gui/camcordialog.ui similarity index 100% rename from llspy/gui/camcordialog.ui rename to src/llspy/gui/camcordialog.ui diff --git a/llspy/gui/exceptions.py b/src/llspy/gui/exceptions.py similarity index 100% rename from llspy/gui/exceptions.py rename to src/llspy/gui/exceptions.py diff --git a/llspy/gui/guiDefaults.ini b/src/llspy/gui/guiDefaults.ini similarity index 100% rename from llspy/gui/guiDefaults.ini rename to src/llspy/gui/guiDefaults.ini diff --git a/llspy/gui/helpers.py b/src/llspy/gui/helpers.py similarity index 100% rename from llspy/gui/helpers.py rename to src/llspy/gui/helpers.py diff --git a/llspy/gui/img_dialog.py b/src/llspy/gui/img_dialog.py similarity index 100% rename from llspy/gui/img_dialog.py rename to src/llspy/gui/img_dialog.py diff --git a/llspy/gui/img_window.py b/src/llspy/gui/img_window.py similarity index 100% rename from llspy/gui/img_window.py rename to src/llspy/gui/img_window.py diff --git a/llspy/gui/img_window.ui b/src/llspy/gui/img_window.ui similarity index 100% rename from llspy/gui/img_window.ui rename to src/llspy/gui/img_window.ui diff --git a/llspy/gui/logo_dark.png b/src/llspy/gui/logo_dark.png similarity index 100% rename from llspy/gui/logo_dark.png rename to src/llspy/gui/logo_dark.png diff --git a/llspy/gui/logo_light.png b/src/llspy/gui/logo_light.png similarity index 100% rename from llspy/gui/logo_light.png rename to src/llspy/gui/logo_light.png diff --git a/llspy/gui/main_gui.py b/src/llspy/gui/main_gui.py similarity index 100% rename from llspy/gui/main_gui.py rename to src/llspy/gui/main_gui.py diff --git a/llspy/gui/main_gui.ui b/src/llspy/gui/main_gui.ui similarity index 100% rename from llspy/gui/main_gui.ui rename to src/llspy/gui/main_gui.ui diff --git a/llspy/gui/mainwindow.py b/src/llspy/gui/mainwindow.py similarity index 100% rename from llspy/gui/mainwindow.py rename to src/llspy/gui/mainwindow.py diff --git a/llspy/gui/qtlogger.py b/src/llspy/gui/qtlogger.py similarity index 100% rename from llspy/gui/qtlogger.py rename to src/llspy/gui/qtlogger.py diff --git a/llspy/gui/styles.py b/src/llspy/gui/styles.py similarity index 100% rename from llspy/gui/styles.py rename to src/llspy/gui/styles.py diff --git a/llspy/gui/watcher.py b/src/llspy/gui/watcher.py similarity index 100% rename from llspy/gui/watcher.py rename to src/llspy/gui/watcher.py diff --git a/llspy/gui/workers.py b/src/llspy/gui/workers.py similarity index 100% rename from llspy/gui/workers.py rename to src/llspy/gui/workers.py diff --git a/llspy/libcudawrapper.py b/src/llspy/libcudawrapper.py similarity index 100% rename from llspy/libcudawrapper.py rename to src/llspy/libcudawrapper.py diff --git a/llspy/libinstall.py b/src/llspy/libinstall.py similarity index 100% rename from llspy/libinstall.py rename to src/llspy/libinstall.py diff --git a/llspy/llsdir.py b/src/llspy/llsdir.py similarity index 100% rename from llspy/llsdir.py rename to src/llspy/llsdir.py diff --git a/llspy/otf.py b/src/llspy/otf.py similarity index 100% rename from llspy/otf.py rename to src/llspy/otf.py diff --git a/llspy/parse.py b/src/llspy/parse.py similarity index 100% rename from llspy/parse.py rename to src/llspy/parse.py diff --git a/llspy/register_old.py b/src/llspy/register_old.py similarity index 100% rename from llspy/register_old.py rename to src/llspy/register_old.py diff --git a/llspy/samples.py b/src/llspy/samples.py similarity index 100% rename from llspy/samples.py rename to src/llspy/samples.py diff --git a/llspy/schema.py b/src/llspy/schema.py similarity index 100% rename from llspy/schema.py rename to src/llspy/schema.py diff --git a/llspy/settingstxt.py b/src/llspy/settingstxt.py similarity index 100% rename from llspy/settingstxt.py rename to src/llspy/settingstxt.py diff --git a/llspy/util.py b/src/llspy/util.py similarity index 100% rename from llspy/util.py rename to src/llspy/util.py diff --git a/llspy/xzpsf.py b/src/llspy/xzpsf.py similarity index 100% rename from llspy/xzpsf.py rename to src/llspy/xzpsf.py From 3edf9f9219b35132e7630845cb18a32ba18b77f8 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 11:15:38 -0500 Subject: [PATCH 10/24] fix test deps --- .github/workflows/ci.yml | 2 +- README.rst | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf1229f..a63f475 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: - name: Install dependencies run: | pip install -U pip - pip install -e .[test] + pip install -e .[test,pyqt5] - name: Test uses: aganders3/headless-gui@v2 diff --git a/README.rst b/README.rst index ce993c1..5e588c6 100755 --- a/README.rst +++ b/README.rst @@ -170,18 +170,15 @@ Requirements Installation ============ -**Note**: *As of version 0.4.2 cudaDecon is now included in the LLSpy conda package and requires no additional steps for installation. Horray for open source!* - - #. Install `conda `_ #. Launch a ``terminal`` window (Linux), or ``Anaconda Prompt`` (Windows) #. Install LLSpy into a new conda environment .. code:: bash - # `-c talley` is required to install the llspylibs package - $ conda create -n llsenv -c talley python=3.11 llspylibs + $ conda create -n llsenv python=3.11 cudadecon $ conda activate llsenv + $ pip install llspy The ``create -n llsenv`` line creates a virtual environment. This is optional, but recommended as it easier to uninstall cleanly and prevents conflicts with any other python environments. If installing into a virtual environment, you must source the environment before proceeding, and each time before using llspy. From 386e75d1610a1a808902d47bab71336c0fd8f671 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Dec 2023 16:15:51 +0000 Subject: [PATCH 11/24] style(pre-commit.ci): auto fixes [...] --- src/llspy/_version.py | 5 +++-- src/llspy/gui/mainwindow.py | 2 +- tests/conftest.py | 1 + tests/test_gui.py | 1 + tests/test_processing.py | 3 ++- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/llspy/_version.py b/src/llspy/_version.py index 41da9aa..60337f0 100644 --- a/src/llspy/_version.py +++ b/src/llspy/_version.py @@ -3,6 +3,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] else: VERSION_TUPLE = object @@ -12,5 +13,5 @@ __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '0.5.1.dev13+gb51b1a7.d20231209' -__version_tuple__ = version_tuple = (0, 5, 1, 'dev13', 'gb51b1a7.d20231209') +__version__ = version = "0.5.1.dev13+gb51b1a7.d20231209" +__version_tuple__ = version_tuple = (0, 5, 1, "dev13", "gb51b1a7.d20231209") diff --git a/src/llspy/gui/mainwindow.py b/src/llspy/gui/mainwindow.py index ccaf6b1..33418bc 100644 --- a/src/llspy/gui/mainwindow.py +++ b/src/llspy/gui/mainwindow.py @@ -6,13 +6,13 @@ import os.path as osp import numpy as np -from fiducialreg.fiducialreg import RegFile, RegistrationError from qtpy import QtCore, QtGui from qtpy import QtWidgets as QtW import llspy import llspy.gui.exceptions as err import llspy.llsdir +from fiducialreg.fiducialreg import RegFile, RegistrationError from llspy.gui import workers from llspy.gui.camcalibgui import CamCalibDialog from llspy.gui.helpers import ( diff --git a/tests/conftest.py b/tests/conftest.py index ed297d2..3b5189b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import pytest + from llspy.libcudawrapper import cudaLib requires_cuda = pytest.mark.skipif(not cudaLib, reason="Cannot test without CUDA") diff --git a/tests/test_gui.py b/tests/test_gui.py index d2f4f2b..851d1ac 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -2,6 +2,7 @@ from pathlib import Path import pytest + from llspy.exceptions import CUDAbinException from llspy.gui.exceptions import MissingBinaryError from llspy.gui.mainwindow import main_GUI diff --git a/tests/test_processing.py b/tests/test_processing.py index 6009988..6b80aac 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -1,8 +1,9 @@ import os +from qtpy import QtCore + from llspy.gui.mainwindow import main_GUI from llspy.llsdir import LLSdir -from qtpy import QtCore from .conftest import requires_cuda From 6c446a85937f33ac71eeeb7fafcbc4f8c9281d0a Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:39:22 -0500 Subject: [PATCH 12/24] try fix ci --- .github/workflows/ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a63f475..11616cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: platform: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: tlambert03/setup-qt-libs@v1 - uses: conda-incubator/setup-miniconda@v3 @@ -44,14 +44,14 @@ jobs: - name: Install dependencies run: | - pip install -U pip - pip install -e .[test,pyqt5] + python -m pip install -U pip + python -m pip install -e .[test,pyqt5] - name: Test uses: aganders3/headless-gui@v2 with: - run: pytest --cov llspy - shell: bash -l {0} + shell: bash -el {0} + run: python -m pytest --cov llspy - name: Coverage uses: codecov/codecov-action@v3 @@ -63,7 +63,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v2 @@ -78,9 +78,7 @@ jobs: python -m build . - name: twine check - run: | - twine check dist/* - ls -lh dist + run: twine check dist/* - name: Build and publish run: twine upload dist/* From a91ae631a0dffb9628116d2eae339e2c7fca4315 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:40:09 -0500 Subject: [PATCH 13/24] remove version --- .gitignore | 2 +- src/llspy/_version.py | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 src/llspy/_version.py diff --git a/.gitignore b/.gitignore index 7f0d84c..48ee0fc 100644 --- a/.gitignore +++ b/.gitignore @@ -119,4 +119,4 @@ tests/testdata/sample/MIPs/ tests/testdata/sample/sample_ProcessingLog.txt tests/testdata/sample/GPUdecon/ -llspy/_version.py +src/llspy/_version.py diff --git a/src/llspy/_version.py b/src/llspy/_version.py deleted file mode 100644 index 60337f0..0000000 --- a/src/llspy/_version.py +++ /dev/null @@ -1,17 +0,0 @@ -# file generated by setuptools_scm -# don't change, don't track in version control -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Tuple, Union - - VERSION_TUPLE = Tuple[Union[int, str], ...] -else: - VERSION_TUPLE = object - -version: str -__version__: str -__version_tuple__: VERSION_TUPLE -version_tuple: VERSION_TUPLE - -__version__ = version = "0.5.1.dev13+gb51b1a7.d20231209" -__version_tuple__ = version_tuple = (0, 5, 1, "dev13", "gb51b1a7.d20231209") From eac09f2462f7e00a9c4c020be5bc9ad3701c3d70 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:44:28 -0500 Subject: [PATCH 14/24] fix extra --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 09ef226..7a59aa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,8 +48,8 @@ dependencies = [ # https://peps.python.org/pep-0621/#dependencies-optional-dependencies [project.optional-dependencies] napari = ["napari"] -pyqt = ["PyQt5"] -pyside = ["PySide2"] +pyqt5 = ["PyQt5"] +pyside2 = ["PySide2"] spimagine = ["spimagine"] test = ["pytest", "pytest-cov", "pytest-qt"] dev = [ From c834e1acc61a88f025fc52c1a15bfdb16c606a9d Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:49:06 -0500 Subject: [PATCH 15/24] fix manifest --- MANIFEST.in | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 8b5ab30..75f63bb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,10 @@ include README.rst include CHANGELOG.rst -recursive-include llspy *.py -recursive-include llspy *.ui -recursive-include llspy *.ini -recursive-include llspy *.png +recursive-include src/llspy *.py +recursive-include src/llspy *.ui +recursive-include src/llspy *.ini +recursive-include src/llspy *.png recursive-include img * recursive-exclude tests * diff --git a/pyproject.toml b/pyproject.toml index 7a59aa9..d00fc20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,4 +97,4 @@ ignore = [ exclude = ["docs/*"] [tool.check-manifest] -ignore = ["llspy/_version.py", ".pre-commit-config.yaml"] +ignore = ["llspy/_version.py", ".pre-commit-config.yaml", "tests/*"] From 96089e0c3e73085178c9c9277ac42a0e508ddd4d Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:56:01 -0500 Subject: [PATCH 16/24] remove np.float --- src/fiducialreg/fiducialreg.py | 4 ++-- src/fiducialreg/imref.py | 2 +- src/fiducialreg/imwarp.py | 2 +- src/llspy/arrayfun.py | 6 +++--- src/llspy/camcalib.py | 8 ++++---- src/llspy/gui/img_dialog.py | 2 +- src/llspy/xzpsf.py | 4 +--- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/fiducialreg/fiducialreg.py b/src/fiducialreg/fiducialreg.py index 7e146b3..b198ed1 100644 --- a/src/fiducialreg/fiducialreg.py +++ b/src/fiducialreg/fiducialreg.py @@ -1052,8 +1052,8 @@ def show_tformed_image(self, movingLabel=None, fixedLabel=None, **kwargs): def imoverlay(im1, im2, method=None, mip=False): - im1 = im1.astype(np.float) if not mip else im1.astype(np.float).max(0) - im2 = im2.astype(np.float) if not mip else im2.astype(np.float).max(0) + im1 = im1.astype(float) if not mip else im1.astype(float).max(0) + im2 = im2.astype(float) if not mip else im2.astype(float).max(0) im1 -= im1.min() im1 /= im1.max() im2 -= im2.min() diff --git a/src/fiducialreg/imref.py b/src/fiducialreg/imref.py index d0e2ab6..5173f75 100644 --- a/src/fiducialreg/imref.py +++ b/src/fiducialreg/imref.py @@ -247,7 +247,7 @@ def __repr__(self): repdict[n] = list(val) else: repdict[n] = [f"{i:.4f}" for i in val] - elif isinstance(self.__getattribute__(n), (np.float, float)): + elif isinstance(self.__getattribute__(n), float): repdict[n] = f"{self.__getattribute__(n):.4f}" elif isinstance(self.__getattribute__(n), (np.int, np.int64, int)): repdict[n] = int(self.__getattribute__(n)) diff --git a/src/fiducialreg/imwarp.py b/src/fiducialreg/imwarp.py index 9ad5a89..53302ea 100644 --- a/src/fiducialreg/imwarp.py +++ b/src/fiducialreg/imwarp.py @@ -33,7 +33,7 @@ def imwarp(inputImage, tform, R_A=None, outputRef=None): # checkOutputViewAgreementWithTform(outputRef,tform) # Resampling the input image must be done in a floating point type. - if not np.issubdtype(inputImage.dtype, np.float): + if not np.issubdtype(inputImage.dtype, float): inputImage = inputImage.astype(np.float64) # Form grid of intrinsic points in output image. diff --git a/src/llspy/arrayfun.py b/src/llspy/arrayfun.py index 5c9240d..04606ed 100644 --- a/src/llspy/arrayfun.py +++ b/src/llspy/arrayfun.py @@ -97,7 +97,7 @@ def imcontentbounds(im, sigma=2): # get rid of the first two planes in case of high dark noise if im.ndim == 3: im = np.squeeze(np.max(im[2:], 0)) - im = im.astype(np.float) + im = im.astype(float) fullwidth = im.shape[-1] # from scipy.ndimage.filters import median_filter # mm = median_filter(b.astype(float),3) @@ -148,14 +148,14 @@ def detect_background(im): im = im[0][2] if im.ndim == 3: im = im[1] # pick the third plane... avoid noise in first plane on lattice - return mode(im.flatten())[0][0] + return mode(im.flatten()).mode def sub_background(im, background=None): """subtract provided background or autodetct as mode of the first plane""" if background is None: background = detect_background(im) - out = im.astype(np.float) - background + out = im.astype(float) - background out[out < 0] = 0 return out diff --git a/src/llspy/camcalib.py b/src/llspy/camcalib.py index d31c10a..4590800 100644 --- a/src/llspy/camcalib.py +++ b/src/llspy/camcalib.py @@ -55,11 +55,11 @@ def combine_stacks(ch0, ch1, darkavg): shp = list(tf.TiffFile(ch0[0]).series[0].shape) nZ = shp[0] shp[0] *= len(ch0) - pre = np.zeros(shp, dtype=np.float) - post = np.zeros(shp, dtype=np.float) + pre = np.zeros(shp, dtype=float) + post = np.zeros(shp, dtype=float) for n in range(len(ch0)): - pre[n * nZ : n * nZ + nZ, :, :] = tf.imread(ch0[n]).astype(np.float) - darkavg - post[n * nZ : n * nZ + nZ, :, :] = tf.imread(ch1[n]).astype(np.float) - darkavg + pre[n * nZ : n * nZ + nZ, :, :] = tf.imread(ch0[n]).astype(float) - darkavg + post[n * nZ : n * nZ + nZ, :, :] = tf.imread(ch1[n]).astype(float) - darkavg return pre, post diff --git a/src/llspy/gui/img_dialog.py b/src/llspy/gui/img_dialog.py index e51c46b..885cb9c 100644 --- a/src/llspy/gui/img_dialog.py +++ b/src/llspy/gui/img_dialog.py @@ -232,7 +232,7 @@ def getCurrent(self): if not self.chanSettings[chan]["active"]: continue lut = self.chanSettings[chan]["lut"] - D = np.maximum(data[chan].astype(np.float) - self.cmin[chan], 0) + D = np.maximum(data[chan].astype(float) - self.cmin[chan], 0) D /= self.cmax[chan] if self.projection is None else D.max() D *= self.chanSettings[chan]["scale"] D = np.tile(D, (3, 1, 1)).transpose(1, 2, 0) diff --git a/src/llspy/xzpsf.py b/src/llspy/xzpsf.py index 96bfa16..bdfe1f3 100755 --- a/src/llspy/xzpsf.py +++ b/src/llspy/xzpsf.py @@ -24,9 +24,7 @@ def main(infile, nx, nz, sig=1, pad=12): maxy, maxx = np.argwhere(mipblur == mipblur.max())[0] print(f"bead detected at ({maxx},{maxy})") - beadslice = indat[:, maxy - pad : maxy + pad, maxx - pad : maxx + pad].astype( - np.float - ) + beadslice = indat[:, maxy - pad : maxy + pad, maxx - pad : maxx + pad].astype(float) background = indat[:, :, 2].mean(1) beadsums = beadslice.sum((1, 2)) - ( 4 * pad * pad * background From 6cbac597241a7dc1d7f0a4cfb1fc24c8ed28cad3 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 12:57:15 -0500 Subject: [PATCH 17/24] remove np.int --- src/fiducialreg/imref.py | 4 ++-- src/llspy/arrayfun.py | 2 +- src/llspy/libcudawrapper.py | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fiducialreg/imref.py b/src/fiducialreg/imref.py index 5173f75..444e0d8 100644 --- a/src/fiducialreg/imref.py +++ b/src/fiducialreg/imref.py @@ -243,13 +243,13 @@ def __repr__(self): if len(val) == 1: repdict[n] = f"{val[0]:.4f}" else: - if np.issubdtype(val.dtype, np.int): + if np.issubdtype(val.dtype, np.integer): repdict[n] = list(val) else: repdict[n] = [f"{i:.4f}" for i in val] elif isinstance(self.__getattribute__(n), float): repdict[n] = f"{self.__getattribute__(n):.4f}" - elif isinstance(self.__getattribute__(n), (np.int, np.int64, int)): + elif isinstance(self.__getattribute__(n), (np.int64, int)): repdict[n] = int(self.__getattribute__(n)) return pformat(repdict) diff --git a/src/llspy/arrayfun.py b/src/llspy/arrayfun.py index 04606ed..3be3403 100644 --- a/src/llspy/arrayfun.py +++ b/src/llspy/arrayfun.py @@ -173,7 +173,7 @@ def deskew_gputools(rawdata, dz=0.5, dx=0.102, angle=31.5, filler=0): (nz, ny, nx) = rawdata.shape # Francois' method: # nxOut = math.ceil((nz - 1) * deskewFactor) + nx - nxOut = np.int(np.floor((nz - 1) * dz * abs(np.cos(angle * np.pi / 180)) / dx) + nx) + nxOut = int(np.floor((nz - 1) * dz * abs(np.cos(angle * np.pi / 180)) / dx) + nx) # +1 to pad left side with 1 column of filler pixels # otherwise, edge pixel values are smeared across the image paddedData = np.ones((nz, ny, nxOut), rawdata.dtype) * filler diff --git a/src/llspy/libcudawrapper.py b/src/llspy/libcudawrapper.py index 3b55591..c1bf226 100644 --- a/src/llspy/libcudawrapper.py +++ b/src/llspy/libcudawrapper.py @@ -181,9 +181,7 @@ def deskewGPU(im, dz=0.5, dr=0.102, angle=31.5, width=0, shift=0, padVal=0.0): im = im.astype(np.float32) # have to calculate this here to know the size of the return array if width == 0: - deskewedNx = np.int( - nx + np.floor(nz * dz * abs(np.cos(angle * np.pi / 180)) / dr) - ) + deskewedNx = int(nx + np.floor(nz * dz * abs(np.cos(angle * np.pi / 180)) / dr)) else: deskewedNx = width From 3bdc62188bc9baa05fa0f801de931932dba9667f Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 13:14:57 -0500 Subject: [PATCH 18/24] updates --- pyproject.toml | 1 + src/llspy/arrayfun.py | 2 +- src/llspy/gui/exceptions.py | 16 +++++++--------- tests/test_processing.py | 9 ++++++--- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d00fc20..7b8ee02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ dependencies = [ "voluptuous", "watchdog", "numba;python_version < '3.12'", + "importlib_metadata; python_version < '3.8'", ] # https://peps.python.org/pep-0621/#dependencies-optional-dependencies diff --git a/src/llspy/arrayfun.py b/src/llspy/arrayfun.py index 3be3403..2b5b793 100644 --- a/src/llspy/arrayfun.py +++ b/src/llspy/arrayfun.py @@ -1,5 +1,5 @@ import numpy as np -from scipy.ndimage.filters import gaussian_filter +from scipy.ndimage import gaussian_filter from scipy.stats import mode from .libcudawrapper import deskewGPU as deskew diff --git a/src/llspy/gui/exceptions.py b/src/llspy/gui/exceptions.py index 21e1398..b1726d7 100644 --- a/src/llspy/gui/exceptions.py +++ b/src/llspy/gui/exceptions.py @@ -36,16 +36,14 @@ def fetch_package_version(dist_name): >>> fetch_package_version('sentry') """ try: - # Importing pkg_resources can be slow, so only import it - # if we need it. - import pkg_resources + from importlib.metadata import version except ImportError: - # pkg_resource is not available on Google App Engine - raise NotImplementedError( - "pkg_resources is not available " "on this Python install" - ) - dist = pkg_resources.get_distribution(dist_name) - return dist.version + from importlib_metadata import version + + try: + return version(dist_name) + except Exception as e: + raise NotImplementedError(f"Could not fetch the version for {dist_name}: {e}") def fetch_git_sha(path, head=None): diff --git a/tests/test_processing.py b/tests/test_processing.py index 6b80aac..90c61f2 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -1,4 +1,5 @@ import os +import shutil from qtpy import QtCore @@ -11,6 +12,9 @@ @requires_cuda def test_basic_processing(qtbot): testdata = os.path.join(os.path.dirname(__file__), "testdata", "sample") + deconFolder = os.path.join(testdata, "GPUdecon") + if os.path.isdir(deconFolder): + shutil.rmtree(deconFolder) LLSdir(testdata).reduce_to_raw(keepmip=False) n_testfiles = len(os.listdir(testdata)) otfdir = os.path.join(os.path.dirname(__file__), "testdata", "otfs") @@ -21,9 +25,8 @@ def test_basic_processing(qtbot): assert mainGUI.listbox.rowCount() == 0 mainGUI.listbox.addPath(testdata) assert mainGUI.listbox.rowCount() == 1 - with qtbot.waitSignal(mainGUI.sig_processing_done, timeout=12000): - qtbot.mouseClick(mainGUI.processButton, QtCore.Qt.LeftButton) - deconFolder = os.path.join(testdata, "GPUdecon") + with qtbot.waitSignal(mainGUI.sig_processing_done, timeout=60000): + mainGUI.onProcess() MIPfolder = os.path.join(deconFolder, "MIPs") assert os.path.isdir(deconFolder) assert os.path.isdir(MIPfolder) From 3a51cd8d51e6359252967f198114281d1b99ffbf Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 13:20:20 -0500 Subject: [PATCH 19/24] pin deps --- pyproject.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b8ee02..7d71bac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,17 +31,17 @@ classifiers = [ "Topic :: Scientific/Engineering :: Visualization", ] dependencies = [ - "click", + "click ~=8.1", "llspy-slm>=0.2.1", "matplotlib", - "numpy", - "parse", - "qtpy", - "scipy", - "sentry-sdk", - "tifffile", - "voluptuous", - "watchdog", + "numpy >=1.22", + "parse ~=1.20", + "qtpy >=2.4", + "scipy ~=1.11", + "sentry-sdk ~=1.38", + "tifffile >=2023.9", + "voluptuous ~=0.14", + "watchdog ~=3.0", "numba;python_version < '3.12'", "importlib_metadata; python_version < '3.8'", ] From 6e73740df5e8e5de4cd4ccc17f24dff6d8ec8884 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 9 Dec 2023 13:39:45 -0500 Subject: [PATCH 20/24] relax scipy --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7d71bac..3b23aff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ "numpy >=1.22", "parse ~=1.20", "qtpy >=2.4", - "scipy ~=1.11", + "scipy <=1.12", "sentry-sdk ~=1.38", "tifffile >=2023.9", "voluptuous ~=0.14", From 267cd7bb6e55cc7efcb855087329e54cf0fd5dcc Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 24 Mar 2024 09:15:47 -0400 Subject: [PATCH 21/24] update ruff --- .pre-commit-config.yaml | 16 ++++------------ pyproject.toml | 12 +++--------- src/fiducialreg/fiducialreg.py | 10 +++------- src/llspy/bin/llspy_gui.py | 4 ++-- src/llspy/camera.py | 4 +--- src/llspy/gui/img_dialog.py | 6 +++--- src/llspy/gui/workers.py | 15 ++++++++------- src/llspy/llsdir.py | 4 +--- src/llspy/settingstxt.py | 4 +--- tests/test_parse.py | 2 +- 10 files changed, 27 insertions(+), 50 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 62f6461..3a9f2e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,27 +12,19 @@ repos: - id: trailing-whitespace - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.15 + rev: v0.16 hooks: - id: validate-pyproject - - repo: https://github.com/asottile/setup-cfg-fmt - rev: v2.5.0 - hooks: - - id: setup-cfg-fmt - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.1.7 + rev: v0.3.4 hooks: - id: ruff args: [--fix, --unsafe-fixes] - - - repo: https://github.com/psf/black - rev: 23.11.0 - hooks: - - id: black + - id: ruff-format # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: v1.6.1 + # rev: v1.9.0 # hooks: # - id: mypy # files: "^src/" diff --git a/pyproject.toml b/pyproject.toml index 3b23aff..ee1ea36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,15 +53,7 @@ pyqt5 = ["PyQt5"] pyside2 = ["PySide2"] spimagine = ["spimagine"] test = ["pytest", "pytest-cov", "pytest-qt"] -dev = [ - "black", - "ipython", - "mypy", - "pdbpp", # https://github.com/pdbpp/pdbpp - "pre-commit", - "rich", # https://github.com/Textualize/rich - "ruff", -] +dev = ["ipython", "mypy", "pdbpp", "pre-commit", "rich", "ruff"] [project.urls] homepage = "https://github.com/tlambert03/LLSpy" @@ -79,6 +71,8 @@ where = ["src"] line-length = 88 target-version = "py37" src = ["src"] + +[tool.ruff.lint] # https://docs.astral.sh/ruff/rules select = [ "E", # style errors diff --git a/src/fiducialreg/fiducialreg.py b/src/fiducialreg/fiducialreg.py index b198ed1..a01c23d 100644 --- a/src/fiducialreg/fiducialreg.py +++ b/src/fiducialreg/fiducialreg.py @@ -46,7 +46,7 @@ matplotlib.use("Qt5Agg") -import matplotlib.pyplot as plt # noqa +import matplotlib.pyplot as plt logger = logging.getLogger(__name__) np.seterr(divide="ignore", invalid="ignore") @@ -94,9 +94,7 @@ def get_thresh(im, mincount=None, steps=100): object_count = np.array(object_count) if mincount > object_count.max(): raise RegistrationError( - "Could not detect minimum number of beads specified ({}), found: {}".format( - mincount, object_count.max() - ) + f"Could not detect minimum number of beads specified ({mincount}), found: {object_count.max()}" ) modecount = stats.mode(object_count[(object_count >= mincount)], axis=None).mode[0] logging.debug( @@ -1174,9 +1172,7 @@ def get_tform(self, moving, ref, mode): ) if mode not in self.tform_dict[ref][moving]: raise RegistrationError( - "Transform mode {} not found for refwave: {}, movingwave: {}".format( - mode, ref, moving - ) + f"Transform mode {mode} not found for refwave: {ref}, movingwave: {moving}" ) return self.tform_dict[ref][moving][mode] diff --git a/src/llspy/bin/llspy_gui.py b/src/llspy/bin/llspy_gui.py index feec003..4a20e10 100644 --- a/src/llspy/bin/llspy_gui.py +++ b/src/llspy/bin/llspy_gui.py @@ -159,9 +159,9 @@ def setOptOut(value): QtWidgets.QMessageBox.information( mainGUI, "Newer Version Available!", - "Update available: v{}\n\nYou are using v{}\n\nIf you are using " + f"Update available: v{newestVersion}\n\nYou are using v{llspy.__version__}\n\nIf you are using " 'anaconda, you may update by typing "conda update -c talley llspy" ' - "at the anaconda prompt".format(newestVersion, llspy.__version__), + "at the anaconda prompt", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton, ) diff --git a/src/llspy/camera.py b/src/llspy/camera.py index f3851b1..a858a9b 100644 --- a/src/llspy/camera.py +++ b/src/llspy/camera.py @@ -85,9 +85,7 @@ def selectiveMedianFilter( 100 * np.sum(pixelMatrix.flatten()) / float(len(pixelMatrix.flatten())) ) print( - "Bad pixels detected: {} {:0.2f}".format( - np.sum(pixelMatrix.flatten()), pixpercent - ) + f"Bad pixels detected: {np.sum(pixelMatrix.flatten())} {pixpercent:0.2f}" ) dt = stack.dtype diff --git a/src/llspy/gui/img_dialog.py b/src/llspy/gui/img_dialog.py index 885cb9c..0d1e17e 100644 --- a/src/llspy/gui/img_dialog.py +++ b/src/llspy/gui/img_dialog.py @@ -11,13 +11,13 @@ matplotlib.use("Qt5Agg") -from matplotlib.backends.backend_qt5agg import ( # noqa: E402 +from matplotlib.backends.backend_qt5agg import ( FigureCanvasQTAgg as FigureCanvas, ) -from matplotlib.backends.backend_qt5agg import ( # noqa: E402 +from matplotlib.backends.backend_qt5agg import ( NavigationToolbar2QT as NavigationToolbar, ) -from matplotlib.figure import Figure # noqa: E402 +from matplotlib.figure import Figure logger = logging.getLogger(__name__) diff --git a/src/llspy/gui/workers.py b/src/llspy/gui/workers.py index 03ea0e9..34e0b2a 100644 --- a/src/llspy/gui/workers.py +++ b/src/llspy/gui/workers.py @@ -63,8 +63,11 @@ def work(self): """ logger.debug(f"Subprocess {self.name} START") self._logger.info( - "~" * 20 + "\nRunning {} thread_{} with args: " - "\n{}\n".format(self.binary, self.id, " ".join(self.args)) + "\n" + "~" * 20 + + "\nRunning {} thread_{} with args: " "\n{}\n".format( + self.binary, self.id, " ".join(self.args) + ) + + "\n" ) self.process.finished.connect(self.onFinished) self.process.finished.connect( @@ -186,9 +189,7 @@ def work(self): break if not self.binary: raise err.MissingBinaryError( - "No binary found for compression program: {}".format( - llspy.compress.EXTENTIONS[tar_extension] - ) + f"No binary found for compression program: {llspy.compress.EXTENTIONS[tar_extension]}" ) self.args = ["-dv", tar_compressed] self.process.finished.connect( @@ -320,8 +321,8 @@ def split(a, n): if len(tRange) == E.parameters.nt: cudaOpts["filename-pattern"] = f"_ch{chan}_" else: - cudaOpts["filename-pattern"] = "_ch{}.*_stack{}".format( - chan, llspy.util.pyrange_to_perlregex(tRange) + cudaOpts["filename-pattern"] = ( + f"_ch{chan}.*_stack{llspy.util.pyrange_to_perlregex(tRange)}" ) argQueue.append(binary.assemble_args(**cudaOpts)) diff --git a/src/llspy/llsdir.py b/src/llspy/llsdir.py index 188b1d3..a6ea08c 100644 --- a/src/llspy/llsdir.py +++ b/src/llspy/llsdir.py @@ -994,9 +994,7 @@ def localParams(self, recalc=False, **kwargs): logger.warning(f"Channel {chan} not present in datset! Excluding.") if np.max(list(_schema.cRange)) > (self.parameters.nc - 1): logger.warning( - "cRange was larger than number of Channels! Excluding C > {}".format( - self.parameters.nc - 1 - ) + f"cRange was larger than number of Channels! Excluding C > {self.parameters.nc - 1}" ) _schema.cRange = outrange diff --git a/src/llspy/settingstxt.py b/src/llspy/settingstxt.py index 35c7a94..ff2967f 100644 --- a/src/llspy/settingstxt.py +++ b/src/llspy/settingstxt.py @@ -144,9 +144,7 @@ def parse(self): continue if self.date is None: logger.error( - "Error, could not parse datestring {} with any of formats {}".format( - datestring, dateformats - ) + f"Error, could not parse datestring {datestring} with any of formats {dateformats}" ) # print that with dateobject.strftime('%x %X %p') diff --git a/tests/test_parse.py b/tests/test_parse.py index 71023f1..6c021be 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -3,7 +3,7 @@ sys.path.append("..") -from llspy import parse # noqa +from llspy import parse class FilenameTests(unittest.TestCase): From 95e879ccc49617a2594f98b3befcb0b74f4a2d47 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 24 Mar 2024 09:19:02 -0400 Subject: [PATCH 22/24] reduce numpy --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ee1ea36..c202fa8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "click ~=8.1", "llspy-slm>=0.2.1", "matplotlib", - "numpy >=1.22", + "numpy >=1.21", "parse ~=1.20", "qtpy >=2.4", "scipy <=1.12", From db42957ea7aedd68eeb25fc0307d964c6d91aa4f Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 24 Mar 2024 09:19:32 -0400 Subject: [PATCH 23/24] change numba --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c202fa8..d1bf714 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ "tifffile >=2023.9", "voluptuous ~=0.14", "watchdog ~=3.0", - "numba;python_version < '3.12'", + "numba;python_version < '3.13'", "importlib_metadata; python_version < '3.8'", ] From 2304b455fe66953ab06f6540a6a99208c36a0a7c Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 24 Mar 2024 09:21:02 -0400 Subject: [PATCH 24/24] unpin numpy and tifffile --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d1bf714..a62d3a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,12 +34,12 @@ dependencies = [ "click ~=8.1", "llspy-slm>=0.2.1", "matplotlib", - "numpy >=1.21", + "numpy", "parse ~=1.20", "qtpy >=2.4", "scipy <=1.12", "sentry-sdk ~=1.38", - "tifffile >=2023.9", + "tifffile", "voluptuous ~=0.14", "watchdog ~=3.0", "numba;python_version < '3.13'",