././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.github/dependabot.yml0000644000000000000000000000076514670536116014167 0ustar00# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.github/workflows/python-package.yml0000644000000000000000000000256114670536116017025 0ustar00# This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: Python package on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel build python -m pip install flake8 if [ -f ci/requirements_gh.txt ]; then pip install -r ci/requirements_gh.txt; fi - name: Build package run: | python -m build - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Run tests run: | python run_tests.py ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.github/workflows/release.yml0000644000000000000000000001137114670536116015532 0ustar00name: Build wheel and deploy on PyPI on: workflow_dispatch: release: types: - published jobs: build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" - name: Install dependencies run: | python -m pip install --upgrade pip pip install build twine - name: Build sdist run: python -m build --sdist - name: Check the package run: python -m twine check dist/* - uses: actions/upload-artifact@v4 with: name: cibw-sdist path: dist/*.tar.gz test_sdist: needs: [build_sdist] name: Test source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" - uses: actions/download-artifact@v4 with: name: cibw-sdist path: dist - name: Install from sdist run: | pip install "$(ls dist/fabio-*.tar.gz)" pip install pyqt5 matplotlib - name: Run tests run: python -c "import fabio.test, sys; sys.exit(fabio.test.run_tests())" build_doc: name: Build documentation runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" - name: Install pandoc&graphviz run: sudo apt-get install pandoc graphviz - name: Install fabio run: pip install . - name: Install documentation dependencies run: pip install -r requirements.txt - name: Build doc env: READTHEDOCS: "True" # To skip checking that fabio is installed locally run: | export FABIO_VERSION="$(python -c 'import fabio; print(fabio.strictversion)')" sphinx-build doc/source/ "fabio-${FABIO_VERSION}_documentation/" zip -r "fabio-${FABIO_VERSION}_documentation.zip" "fabio-${FABIO_VERSION}_documentation/" - uses: actions/upload-artifact@v4 with: name: documentation path: fabio-*_documentation.zip build_wheels: name: Build wheels on ${{ matrix.os }}-${{ matrix.cibw_archs }} runs-on: ${{ matrix.os }} strategy: # Ensure that a wheel builder finishes even if another fails fail-fast: false matrix: include: - os: ubuntu-20.04 cibw_archs: "auto64" - os: ubuntu-20.04 cibw_archs: "aarch64" - os: ubuntu-20.04 cibw_archs: "ppc64le" - os: windows-2019 cibw_archs: "auto64" - os: macos-12 cibw_archs: "x86_64" macos_target: "11.0" - os: macos-14 cibw_archs: "arm64" macos_target: "11.0" steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 if: runner.os == 'Linux' with: platforms: all - uses: pypa/cibuildwheel@v2.16.5 env: # Use silx wheelhouse: needed for ppc64le CIBW_ENVIRONMENT_LINUX: "PIP_FIND_LINKS=https://www.silx.org/pub/wheelhouse/ PIP_TRUSTED_HOST=www.silx.org" CIBW_BUILD_VERBOSITY: 1 CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-* # Do not build for pypy and muslinux CIBW_SKIP: pp* *-musllinux_* CIBW_ARCHS: ${{ matrix.cibw_archs }} MACOSX_DEPLOYMENT_TARGET: "${{ matrix.macos_target }}" # Install test dependencies CIBW_TEST_COMMAND: python -c "import fabio.test, sys; sys.exit(fabio.test.run_tests())" # Skip tests for emulated architectures # and Python3.8 on macos/arm64 (https://github.com/pypa/cibuildwheel/pull/1169) CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x} cp38-macosx_*" - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl pypi-publish: needs: [build_doc, build_sdist, build_wheels, test_sdist] name: Upload release to PyPI runs-on: ubuntu-latest environment: name: pypi permissions: id-token: write if: github.event_name == 'release' && github.event.action == 'published' # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this) # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/download-artifact@v4 with: pattern: cibw-* path: dist merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.gitignore0000644000000000000000000000066114670536116011762 0ustar00*.py[cod] # C extensions *.so #testimages test/testimages # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject .settings # generated c files, i.e. all except the ones in the src directory *.c !fabio/ext/src/*.c ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.pre-commit-config.yaml0000644000000000000000000000055314670536116014253 0ustar00# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v3.2.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-json - id: check-toml - id: check-added-large-files ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/.readthedocs.yaml0000644000000000000000000000124614670536116013221 0ustar00# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.10" # Build documentation in the docs/ directory with Sphinx sphinx: builder: html configuration: doc/source/conf.py # Optionally build your docs in additional formats such as PDF #formats: # - pdf # Optionally declare the Python requirements required to build your docs python: install: - requirements: requirements.txt - requirements: ci/requirements_rtd.txt - method: pip path: . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/CHANGELOG.rst0000644000000000000000000000004314670536116012005 0ustar00Please see doc/sources/Changlog.rst././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/README.rst0000644000000000000000000001475514670536116011472 0ustar00FabIO: Fable Input/Output library ================================= Main websites: * https://github.com/silx-kit/fabio * http://fable.sf.net (historical) |Build Status| |Appveyor Status| ---- FabIO is an I/O library for images produced by 2D X-ray detectors and written in Python. FabIO support images detectors from a dozen of companies (including Mar, Dectris, ADSC, Hamamatsu, Oxford, ...), for a total of 30 different file formats (like CBF, EDF, TIFF, ...) and offers an unified interface to their headers (as a Python dictionary) and datasets (as a numpy ndarray of integers or floats) .. contents:: :depth: 1 Installation ------------ FabIO is available from `PyPI `_: ``pip install fabio`` `Debian/Ubuntu packages `_, and `wheels `_ are available for Windows, Linux and MacOSX from the silx repository. See the `installation instructions `_ for more information. Usage ----- Open an image ............. >>> import fabio >>> obj = fabio.open("mydata0000.edf") >>> obj.data.shape (2048, 2048) >>> obj.header["Omega"] 23.5 >>> obj.data array([...]) Save an image (ex: EDF) ....................... >>> import fabio >>> obj = fabio.edfimage.EdfImage(data=[...]) >>> obj.write("mydata0000.edf") Documentation ------------- See the `latest release documentation `_ for further details. Documentation of previous versions are available on `silx `_. Changelog --------- See http://www.silx.org/doc/fabio/latest/Changelog.html Citation -------- The general philosophy of the library is described in: `FabIO: easy access to two-dimensional X-ray detector images in Python; E. B. Knudsen, H. O. Sørensen, J. P. Wright, G. Goret and J. Kieffer Journal of Applied Crystallography, Volume 46, Part 2, pages 537-539. `_ Transparent handling of compressed files ---------------------------------------- For FabIO to handle gzip and bzip2 compressed files transparently, ``bzip`` and ``gzip`` modules must be present when installing/building Python (e.g. ``libbz2-dev`` package for Ubuntu). Benchmarking details have been collected at http://www.silx.org/doc/fabio/latest/performances.html. Supported file formats ---------------------- * ADSC: + AdscImage * Bruker: + BrukerImage + Bruker100Image + KcdImage: Nonius KappaCCD diffractometer * D3M + D3mImage * Dectris: + CbfImage (implements a fast byte offset de/compression scheme in python/cython) + PilatusImage (fileformat derived from Tiff) + EigerImage (derived from HDF5/NeXus format, depends on `h5py`) * ESRF: + EdfImage: The ESRF data Format + XsdImage: XML serialized image from EDNA + Fit2dImage: Fit2d binary format + Fit2dmaskImage: Fit2d Mask format + Fit2dSpreadsheetImage: Fit2d ascii tables (spread-sheet) + LimaImage: image stacks written by the LImA aquisition system + SparseImage: single crystal diffractions images written by pyFAI * General Electrics + GEimage (including support for variant used at APS) * Hamamatsu + HiPiCImage * HDF5: generic format for stack of images based on h5py + Hdf5Image + EigerImage + LimaImage + SparseImage * JPEG image format: + JPEG using PIL + JPEG 2000 using Glymur * Mar Research: + MarccdImage (fileformat derived from Tiff) + Mar345Image imaging plate with PCK compression * MPA multiwire + MpaImage * Medical Research Council file format for 3D electron density and 2D images + MrcImage * Nonius -> now owned by Bruker + KcdImage * Numpy: generic reader for 2D arrays saved + NumpyImage * Oxford Diffraction Sapphire 3 + OXDimage uncompressed or with TY1 or TY5 compression scheme + Esperanto format (with bitfield compression) * Pixirad Imaging + PixiImage * PNM + PnmImage * Princeton Instrument SPE + SpeImage * Raw Binary without compression * Rigaku + RaxisImage + DtrekImage * Tiff + TifImage using either: - Pillow (external dependency) - TiffIO taken from PyMca Design Specifications --------------------- Name: ..... FabIO = Fable Input/Output Idea: ..... Have a base class for all our 2D diffraction greyscale images. This consists of a 2D array (numpy ndarray) and a python dictionary (actually an ordered dict) of header information in (string key, string value) pairs. Class FabioImage ................ Needs a name which will not to be confused with an RGB color image. Class attributes, often exposed as properties: * data -> 2D array * header -> ordered dictionary * rows, columns, dim1, dim2 -> data.shape (propertiy) * header_keys -> property for list(header.keys()), formerly used to retain the order of the header * bytecode -> data.typecode() (property) * m, minval, maxval, stddev -> image statistics, could add others, eg roi[slice] Class methods (functions): * integrate_area() -> return sum(self.data) within slice * rebin(fact) -> rebins data, adjusts dims * toPIL16() -> returns a PILimage * getheader() -> returns self.header * resetvals() -> resets the statistics * getmean() -> (computes) returns self.m * getmin() -> (computes) returns self.minval * getmax() -> (computes) returns self.maxval * getstddev() -> (computes) returns self.stddev * read() -> read image from file [or stream, or shared memory] * write() -> write image to file [or stream, or shared memory] * readheader() -> read only the header [much faster for scanning files] Each individual file format would then inherit all the functionality of this class and just make new read and write methods. There are also fileseries related methods (next(), previous(), ...) which returns a FabioImage instance of the next/previous frame in a fileserie Other feature: * possibility for using on-the-fly external compression - i.e. if files are stored as something as .gz, .bz2 etc could decompress them, using an external compression mechanism (if available). .. |Build Status| image:: https://travis-ci.org/silx-kit/fabio.svg?branch=master :target: https://travis-ci.org/silx-kit/fabio .. |Appveyor Status| image:: https://ci.appveyor.com/api/projects/status/4k6lol1vq30qhf66/branch/master?svg=true :target: https://ci.appveyor.com/project/ESRF/fabio/branch/master ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726135374.0 fabio-2024.9.0/bootstrap.py0000755000000000000000000001722014670536116012363 0ustar00#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Bootstrap helps you to test scripts without installing them by patching your PYTHONPATH on the fly example: ./bootstrap.py ipython """ __authors__ = ["Frédéric-Emmanuel Picca", "Jérôme Kieffer"] __contact__ = "jerome.kieffer@esrf.eu" __license__ = "MIT" __date__ = "11/04/2024" import sys import os import subprocess import logging if sys.version_info[:2] < (3, 11): import tomli else: import tomllib as tomli logging.basicConfig() logger = logging.getLogger("bootstrap") def get_project_name(root_dir): """Retrieve project name by running python setup.py --name in root_dir. :param str root_dir: Directory where to run the command. :return: The name of the project stored in root_dir """ logger.debug("Getting project name in %s", root_dir) with open("pyproject.toml") as f: pyproject = tomli.loads(f.read()) return pyproject.get("project", {}).get("name") def build_project(name, root_dir): """Build locally the project using meson :param str name: Name of the project. :param str root_dir: Root directory of the project :return: The path to the directory were build was performed """ extra = [] libdir = "lib" if sys.platform == "win32": libdir = "Lib" # extra = ["--buildtype", "plain"] build = os.path.join(root_dir, "build") if not(os.path.isdir(build) and os.path.isdir(os.path.join(build, name))): p = subprocess.Popen(["meson", "setup", "build"], shell=False, cwd=root_dir, env=os.environ) p.wait() p = subprocess.Popen(["meson", "configure", "--prefix", "/"] + extra, shell=False, cwd=build, env=os.environ) p.wait() p = subprocess.Popen(["meson", "install", "--destdir", "."], shell=False, cwd=build, env=os.environ) logger.debug("meson install ended with rc= %s", p.wait()) home = None if os.environ.get("PYBUILD_NAME") == name: # we are in the debian packaging way home = os.environ.get("PYTHONPATH", "").split(os.pathsep)[-1] if not home: if os.environ.get("BUILDPYTHONPATH"): home = os.path.abspath(os.environ.get("BUILDPYTHONPATH", "")) else: if sys.platform == "win32": home = os.path.join(build, libdir, "site-packages") else: python_version = f"python{sys.version_info.major}.{sys.version_info.minor}" home = os.path.join(build, libdir, python_version, "site-packages") home = os.path.abspath(home) cnt = 0 while not os.path.isdir(home): cnt += 1 home = os.path.split(home)[0] for _ in range(cnt): n = os.listdir(home)[0] home = os.path.join(home, n) logger.warning("Building %s to %s", name, home) return home def execfile(fullpath, globals=None, locals=None): "Python3 implementation for execfile" with open(fullpath) as f: try: data = f.read() except UnicodeDecodeError: raise SyntaxError("Not a Python script") code = compile(data, fullpath, 'exec') exec(code, globals, locals) def run_file(filename, argv): """ Execute a script trying first to use execfile, then a subprocess :param str filename: Script to execute :param list[str] argv: Arguments passed to the filename """ full_args = [filename] full_args.extend(argv) try: logger.info("Execute target using exec") # execfile is considered as a local call. # Providing globals() as locals will force to feed the file into # globals() (for examples imports). # Without this any function call from the executed file loses imports try: old_argv = sys.argv sys.argv = full_args logger.info("Patch the sys.argv: %s", sys.argv) logger.info("Executing %s.main()", filename) print("########### EXECFILE ###########") module_globals = globals().copy() module_globals['__file__'] = filename execfile(filename, module_globals, module_globals) finally: sys.argv = old_argv except SyntaxError as error: logger.error(error) logger.info("Execute target using subprocess") env = os.environ.copy() env.update({"PYTHONPATH": LIBPATH + os.pathsep + os.environ.get("PYTHONPATH", ""), "PATH": os.environ.get("PATH", "")}) print("########### SUBPROCESS ###########") run = subprocess.Popen(full_args, shell=False, env=env) run.wait() def run_entry_point(target_name, entry_point, argv): """ Execute an entry_point using the current python context :param str entry_point: A string identifying a function from a module (NAME = PACKAGE.MODULE:FUNCTION) :param argv: list of arguments """ import importlib elements = entry_point.split(":") module_name = elements[0].strip() function_name = elements[1].strip() logger.info("Execute target %s (function %s from module %s) using importlib", target_name, function_name, module_name) full_args = [target_name] full_args.extend(argv) try: old_argv = sys.argv sys.argv = full_args print("########### IMPORTLIB ###########") module = importlib.import_module(module_name) if hasattr(module, function_name): func = getattr(module, function_name) func() else: logger.info("Function %s not found", function_name) finally: sys.argv = old_argv def find_executable(target): """Find a filename from a script name. - Check the script name as file path, - Then checks if the name is a target of the setup.py - Then search the script from the PATH environment variable. :param str target: Name of the script :returns: Returns a tuple: kind, name. """ if os.path.isfile(target): return ("path", os.path.abspath(target)) # search the executable in pyproject.toml with open(os.path.join(PROJECT_DIR, "pyproject.toml")) as f: pyproject = tomli.loads(f.read()) for script, entry_point in list(pyproject.get("console_scripts", {}).items()) + list(pyproject.get("gui_scripts", {}).items()): if script == target: print(script, entry_point) return ("entry_point", target, entry_point) return None, None PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_NAME = get_project_name(PROJECT_DIR) logger.info("Project name: %s", PROJECT_NAME) if __name__ == "__main__": LIBPATH = build_project(PROJECT_NAME, PROJECT_DIR) if len(sys.argv) < 2: logger.warning("usage: ./bootstrap.py