From c8b4e947b209035c7d08c84b04b5f912147982f4 Mon Sep 17 00:00:00 2001 From: Toby A Date: Wed, 6 Aug 2025 14:01:23 +0100 Subject: [PATCH 01/12] more detailed error descriptions including line numbers --- topostats/processing.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/topostats/processing.py b/topostats/processing.py index 80ee9db1231..50f1d4b6d57 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -1,6 +1,6 @@ """Functions for processing data.""" -import logging +import logging, sys from collections import defaultdict from pathlib import Path @@ -384,9 +384,11 @@ def run_grainstats( LOGGER.info(f"[{filename}] : Calculated grainstats for {len(grainstats_df)} grains.") LOGGER.info(f"[{filename}] : Grainstats stage completed successfully.") return grainstats_df, height_profiles_dict, grainstats_calculator.grain_crops - except Exception: - LOGGER.info( - f"[{filename}] : Errors occurred whilst calculating grain statistics. Returning empty dataframe." + except Exception as e: + _, _, e_traceback = sys.exc_info() + e_line_no = e_traceback.tb_lineno + LOGGER.error( + f"[{filename}] : An error occurred whilst calculating grain statistics on line {e_line_no}: {e}\nReturning empty dataframe." ) return create_empty_dataframe(column_set="grainstats"), height_profiles_dict, {} else: From 8cc5fcae598e3d7c9f4b15f3c4c9d9de48b40757 Mon Sep 17 00:00:00 2001 From: Toby A Date: Wed, 6 Aug 2025 14:12:37 +0100 Subject: [PATCH 02/12] more detailed error messaging in processing.py, including line number of error --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a44520aee8..fd976516446 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.12.2 + rev: v0.12.7 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] From b1e1d2e6d8faaf3f9a9398701ce361a64fd89898 Mon Sep 17 00:00:00 2001 From: Toby A Date: Wed, 6 Aug 2025 14:26:19 +0100 Subject: [PATCH 03/12] added missing 'continue' to an if statement checking size of subgrains --- topostats/grainstats.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/topostats/grainstats.py b/topostats/grainstats.py index 53b325abe59..fe917d3256e 100644 --- a/topostats/grainstats.py +++ b/topostats/grainstats.py @@ -25,7 +25,7 @@ # pylint: disable=line-too-long # pylint: disable=fixme # FIXME : The calculate_stats() and calculate_aspect_ratio() raise this error when linting, could consider putting -# variables into dictionar, see example of breaking code out to staticmethod extremes() and returning a +# variables into dictionary, see example of breaking code out to staticmethod extremes() and returning a # dictionary of x_min/x_max/y_min/y_max # pylint: disable=too-many-locals # FIXME : calculate_aspect_ratio raises this error when linting it has 65 statements, recommended not to exceed 50. @@ -238,6 +238,7 @@ def calculate_stats(self) -> tuple[pd.DataFrame, dict]: f"[{self.image_name}] : Skipping subgrain due to being too small " f"(size: {subgrain_tight_shape}) to calculate stats for." ) + continue # Calculate all the stats points = self.calculate_points(subgrain_only_mask) From 90026f65f48e04a46dc04ff06c42faaef9f38d2c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:30:44 +0000 Subject: [PATCH 04/12] [pre-commit.ci] Fixing issues with pre-commit --- topostats/processing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/topostats/processing.py b/topostats/processing.py index 50f1d4b6d57..3dd556f0d33 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -1,6 +1,7 @@ """Functions for processing data.""" -import logging, sys +import logging +import sys from collections import defaultdict from pathlib import Path From a59025b9e1b8765770e1a4ae50114c2737c0a81a Mon Sep 17 00:00:00 2001 From: Toby A Date: Mon, 11 Aug 2025 12:18:09 +0100 Subject: [PATCH 05/12] include topostats version in output config file --- topostats/io.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/topostats/io.py b/topostats/io.py index e9ed309090d..48196466b02 100644 --- a/topostats/io.py +++ b/topostats/io.py @@ -10,6 +10,7 @@ from collections.abc import MutableMapping from datetime import datetime from importlib import resources +from importlib.metadata import version from pathlib import Path from typing import Any, TypeVar @@ -237,6 +238,17 @@ def write_yaml( header = f"# {header_message} : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE else: header = f"# Configuration from TopoStats run completed : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE + + # Add comment to config with topostats version + commit + topostats_details = version("topostats").split("+g") + topostats_version = topostats_details[0] + topostats_commit = "+g" + topostats_details[1].split(".d")[0] + topostats_date = topostats_details[1].split(".d")[1] + topostats_date_formatted = f"{topostats_date[:4]}-{topostats_date[4:6]}-{topostats_date[6:]}" + header += f"# TopoStats version: {topostats_version}\n" + header += f"# Commit: {topostats_commit}\n" + header += f"# Commit date: {topostats_date_formatted}\n" + output_config.write_text(header, encoding="utf-8") yaml = YAML(typ="safe") From 70b0c2b4c837c2abe1b32831de33858eb5edf3e6 Mon Sep 17 00:00:00 2001 From: Toby A Date: Mon, 11 Aug 2025 16:47:20 +0100 Subject: [PATCH 06/12] included version statement in log files and completion message --- topostats/__init__.py | 10 ++++++++++ topostats/entry_point.py | 5 ++++- topostats/io.py | 13 +++---------- topostats/processing.py | 5 +++-- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/topostats/__init__.py b/topostats/__init__.py index fd282f998e9..4412bae342c 100644 --- a/topostats/__init__.py +++ b/topostats/__init__.py @@ -23,6 +23,10 @@ __version__ = version("topostats") __release__ = ".".join(__version__.split(".")[:-2]) +TOPOSTATS_DETAILS = version("topostats").split("+g") +TOPOSTATS_VERSION = TOPOSTATS_DETAILS[0] +TOPOSTATS_COMMIT = TOPOSTATS_DETAILS[1].split(".d")[0] + colormaps.register(cmap=Colormap("nanoscope").get_cmap()) colormaps.register(cmap=Colormap("gwyddion").get_cmap()) @@ -269,3 +273,9 @@ def topostats_to_dict(self) -> dict[str, str | ImageGrainCrops | npt.NDArray]: Dictionary of ''TopoStats'' object. """ return {re.sub(r"^_", "", key): value for key, value in self.__dict__.items()} + + +def log_topostats_version() -> None: + """Log the TopoStats version, commit and date to system logger.""" + LOGGER.info(f"TopoStats version : {TOPOSTATS_VERSION}") + LOGGER.info(f"Commit : {TOPOSTATS_COMMIT}") diff --git a/topostats/entry_point.py b/topostats/entry_point.py index 35a34a8da40..c574ddd5208 100644 --- a/topostats/entry_point.py +++ b/topostats/entry_point.py @@ -8,7 +8,7 @@ import sys from pathlib import Path -from topostats import __version__, run_modules +from topostats import __version__, log_topostats_version, run_modules from topostats.io import write_config_with_comments from topostats.plotting import run_toposum @@ -1268,6 +1268,9 @@ def entry_point(manually_provided_args=None, testing=False) -> None: None Does not return anything. """ + # Log topostats version and the commit id + log_topostats_version() + # Parse command line options, load config (or default) and update with command line options parser = create_parser() args = parser.parse_args() if manually_provided_args is None else parser.parse_args(manually_provided_args) diff --git a/topostats/io.py b/topostats/io.py index 48196466b02..d1c43b1b582 100644 --- a/topostats/io.py +++ b/topostats/io.py @@ -10,7 +10,6 @@ from collections.abc import MutableMapping from datetime import datetime from importlib import resources -from importlib.metadata import version from pathlib import Path from typing import Any, TypeVar @@ -22,7 +21,7 @@ from numpyencoder import NumpyEncoder from ruamel.yaml import YAML, YAMLError -from topostats import __release__, grains +from topostats import TOPOSTATS_COMMIT, TOPOSTATS_DETAILS, __release__, grains from topostats.logs.logs import LOGGER_NAME LOGGER = logging.getLogger(LOGGER_NAME) @@ -240,14 +239,8 @@ def write_yaml( header = f"# Configuration from TopoStats run completed : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE # Add comment to config with topostats version + commit - topostats_details = version("topostats").split("+g") - topostats_version = topostats_details[0] - topostats_commit = "+g" + topostats_details[1].split(".d")[0] - topostats_date = topostats_details[1].split(".d")[1] - topostats_date_formatted = f"{topostats_date[:4]}-{topostats_date[4:6]}-{topostats_date[6:]}" - header += f"# TopoStats version: {topostats_version}\n" - header += f"# Commit: {topostats_commit}\n" - header += f"# Commit date: {topostats_date_formatted}\n" + header += f"# TopoStats version: {TOPOSTATS_DETAILS[0]}\n" + header += f"# Commit: {TOPOSTATS_COMMIT}\n" output_config.write_text(header, encoding="utf-8") diff --git a/topostats/processing.py b/topostats/processing.py index 3dd556f0d33..6046ae227e4 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -10,7 +10,7 @@ import pandas as pd from art import tprint -from topostats import __version__ +from topostats import TOPOSTATS_COMMIT, TOPOSTATS_VERSION from topostats.array_manipulation import re_crop_grain_image_and_mask_to_set_size_nm from topostats.filters import Filters from topostats.grains import GrainCrop, GrainCropsDirection, Grains, ImageGrainCrops @@ -1629,7 +1629,8 @@ def completion_message(config: dict, img_files: list, summary_config: dict, imag tprint("TopoStats", font="twisted") LOGGER.info( f"\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLETE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n" - f" TopoStats Version : {__version__}\n" + f" TopoStats Version : {TOPOSTATS_VERSION}\n" + f" TopoStats Commit : {TOPOSTATS_COMMIT}\n" f" Base Directory : {config['base_dir']}\n" f" File Extension : {config['file_ext']}\n" f" Files Found : {len(img_files)}\n" From d7739986b53a9b4cdc9db2628e8d815677d57a50 Mon Sep 17 00:00:00 2001 From: Neil Shephard Date: Tue, 12 Aug 2025 10:53:34 +0100 Subject: [PATCH 07/12] ci: update to actions/checkout@v5 New version of the [`action/checkout v5.0.0`](https://github.com/actions/checkout/releases) so updating workflows. --- .github/workflows/pypi.yaml | 2 +- .github/workflows/sphinx_docs_to_gh_pages.yaml | 2 +- .github/workflows/tests.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pypi.yaml b/.github/workflows/pypi.yaml index 4720d152820..89c162657ad 100644 --- a/.github/workflows/pypi.yaml +++ b/.github/workflows/pypi.yaml @@ -12,7 +12,7 @@ jobs: name: Build Package runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Setup Python diff --git a/.github/workflows/sphinx_docs_to_gh_pages.yaml b/.github/workflows/sphinx_docs_to_gh_pages.yaml index 59db9471c43..947b8d618b8 100644 --- a/.github/workflows/sphinx_docs_to_gh_pages.yaml +++ b/.github/workflows/sphinx_docs_to_gh_pages.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest name: Sphinx docs to gh-pages steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Setup Python diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 5676b79a59c..e458b249b6d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -30,7 +30,7 @@ jobs: os: ["ubuntu-latest", "macos-latest", "windows-latest"] python-version: ["3.10", "3.11"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 with: From 9976169042524df5d6ffab697a58401e6220aafb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 16:10:38 +0000 Subject: [PATCH 08/12] build(deps): bump actions/download-artifact Bumps the actions group with 1 update in the / directory: [actions/download-artifact](https://github.com/actions/download-artifact). Updates `actions/download-artifact` from 4 to 5 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/pypi.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi.yaml b/.github/workflows/pypi.yaml index 89c162657ad..e39fddc9b36 100644 --- a/.github/workflows/pypi.yaml +++ b/.github/workflows/pypi.yaml @@ -46,7 +46,7 @@ jobs: url: https://pypi.org/p/topostats steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: python-package-distributions path: dist/ @@ -72,7 +72,7 @@ jobs: # steps: # - name: Download all the dists - # uses: actions/download-artifact@v4 + # uses: actions/download-artifact@v5 # with: # name: python-package-distributions # path: dist/ From 2c0d4963ef832c893c23a740400f032d0e68933a Mon Sep 17 00:00:00 2001 From: Toby A Date: Thu, 14 Aug 2025 09:41:56 +0100 Subject: [PATCH 09/12] cleans the version logging of topostats --- topostats/__init__.py | 10 ---------- topostats/entry_point.py | 5 +---- topostats/io.py | 6 +----- topostats/processing.py | 3 --- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/topostats/__init__.py b/topostats/__init__.py index 4412bae342c..fd282f998e9 100644 --- a/topostats/__init__.py +++ b/topostats/__init__.py @@ -23,10 +23,6 @@ __version__ = version("topostats") __release__ = ".".join(__version__.split(".")[:-2]) -TOPOSTATS_DETAILS = version("topostats").split("+g") -TOPOSTATS_VERSION = TOPOSTATS_DETAILS[0] -TOPOSTATS_COMMIT = TOPOSTATS_DETAILS[1].split(".d")[0] - colormaps.register(cmap=Colormap("nanoscope").get_cmap()) colormaps.register(cmap=Colormap("gwyddion").get_cmap()) @@ -273,9 +269,3 @@ def topostats_to_dict(self) -> dict[str, str | ImageGrainCrops | npt.NDArray]: Dictionary of ''TopoStats'' object. """ return {re.sub(r"^_", "", key): value for key, value in self.__dict__.items()} - - -def log_topostats_version() -> None: - """Log the TopoStats version, commit and date to system logger.""" - LOGGER.info(f"TopoStats version : {TOPOSTATS_VERSION}") - LOGGER.info(f"Commit : {TOPOSTATS_COMMIT}") diff --git a/topostats/entry_point.py b/topostats/entry_point.py index c574ddd5208..35a34a8da40 100644 --- a/topostats/entry_point.py +++ b/topostats/entry_point.py @@ -8,7 +8,7 @@ import sys from pathlib import Path -from topostats import __version__, log_topostats_version, run_modules +from topostats import __version__, run_modules from topostats.io import write_config_with_comments from topostats.plotting import run_toposum @@ -1268,9 +1268,6 @@ def entry_point(manually_provided_args=None, testing=False) -> None: None Does not return anything. """ - # Log topostats version and the commit id - log_topostats_version() - # Parse command line options, load config (or default) and update with command line options parser = create_parser() args = parser.parse_args() if manually_provided_args is None else parser.parse_args(manually_provided_args) diff --git a/topostats/io.py b/topostats/io.py index d1c43b1b582..d2028b2d14d 100644 --- a/topostats/io.py +++ b/topostats/io.py @@ -21,7 +21,7 @@ from numpyencoder import NumpyEncoder from ruamel.yaml import YAML, YAMLError -from topostats import TOPOSTATS_COMMIT, TOPOSTATS_DETAILS, __release__, grains +from topostats import __release__, grains from topostats.logs.logs import LOGGER_NAME LOGGER = logging.getLogger(LOGGER_NAME) @@ -238,10 +238,6 @@ def write_yaml( else: header = f"# Configuration from TopoStats run completed : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE - # Add comment to config with topostats version + commit - header += f"# TopoStats version: {TOPOSTATS_DETAILS[0]}\n" - header += f"# Commit: {TOPOSTATS_COMMIT}\n" - output_config.write_text(header, encoding="utf-8") yaml = YAML(typ="safe") diff --git a/topostats/processing.py b/topostats/processing.py index 6046ae227e4..9615d053504 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -10,7 +10,6 @@ import pandas as pd from art import tprint -from topostats import TOPOSTATS_COMMIT, TOPOSTATS_VERSION from topostats.array_manipulation import re_crop_grain_image_and_mask_to_set_size_nm from topostats.filters import Filters from topostats.grains import GrainCrop, GrainCropsDirection, Grains, ImageGrainCrops @@ -1629,8 +1628,6 @@ def completion_message(config: dict, img_files: list, summary_config: dict, imag tprint("TopoStats", font="twisted") LOGGER.info( f"\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLETE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n" - f" TopoStats Version : {TOPOSTATS_VERSION}\n" - f" TopoStats Commit : {TOPOSTATS_COMMIT}\n" f" Base Directory : {config['base_dir']}\n" f" File Extension : {config['file_ext']}\n" f" Files Found : {len(img_files)}\n" From 4a971ace8a5d3adbf930a02f8e11f6f3fde2db54 Mon Sep 17 00:00:00 2001 From: Toby A Date: Thu, 14 Aug 2025 10:20:30 +0100 Subject: [PATCH 10/12] cleaning up topostats version logging --- topostats/__init__.py | 10 ++++++++++ topostats/entry_point.py | 5 ++++- topostats/io.py | 6 +++++- topostats/processing.py | 3 +++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/topostats/__init__.py b/topostats/__init__.py index fd282f998e9..4412bae342c 100644 --- a/topostats/__init__.py +++ b/topostats/__init__.py @@ -23,6 +23,10 @@ __version__ = version("topostats") __release__ = ".".join(__version__.split(".")[:-2]) +TOPOSTATS_DETAILS = version("topostats").split("+g") +TOPOSTATS_VERSION = TOPOSTATS_DETAILS[0] +TOPOSTATS_COMMIT = TOPOSTATS_DETAILS[1].split(".d")[0] + colormaps.register(cmap=Colormap("nanoscope").get_cmap()) colormaps.register(cmap=Colormap("gwyddion").get_cmap()) @@ -269,3 +273,9 @@ def topostats_to_dict(self) -> dict[str, str | ImageGrainCrops | npt.NDArray]: Dictionary of ''TopoStats'' object. """ return {re.sub(r"^_", "", key): value for key, value in self.__dict__.items()} + + +def log_topostats_version() -> None: + """Log the TopoStats version, commit and date to system logger.""" + LOGGER.info(f"TopoStats version : {TOPOSTATS_VERSION}") + LOGGER.info(f"Commit : {TOPOSTATS_COMMIT}") diff --git a/topostats/entry_point.py b/topostats/entry_point.py index 35a34a8da40..c574ddd5208 100644 --- a/topostats/entry_point.py +++ b/topostats/entry_point.py @@ -8,7 +8,7 @@ import sys from pathlib import Path -from topostats import __version__, run_modules +from topostats import __version__, log_topostats_version, run_modules from topostats.io import write_config_with_comments from topostats.plotting import run_toposum @@ -1268,6 +1268,9 @@ def entry_point(manually_provided_args=None, testing=False) -> None: None Does not return anything. """ + # Log topostats version and the commit id + log_topostats_version() + # Parse command line options, load config (or default) and update with command line options parser = create_parser() args = parser.parse_args() if manually_provided_args is None else parser.parse_args(manually_provided_args) diff --git a/topostats/io.py b/topostats/io.py index d2028b2d14d..d1c43b1b582 100644 --- a/topostats/io.py +++ b/topostats/io.py @@ -21,7 +21,7 @@ from numpyencoder import NumpyEncoder from ruamel.yaml import YAML, YAMLError -from topostats import __release__, grains +from topostats import TOPOSTATS_COMMIT, TOPOSTATS_DETAILS, __release__, grains from topostats.logs.logs import LOGGER_NAME LOGGER = logging.getLogger(LOGGER_NAME) @@ -238,6 +238,10 @@ def write_yaml( else: header = f"# Configuration from TopoStats run completed : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE + # Add comment to config with topostats version + commit + header += f"# TopoStats version: {TOPOSTATS_DETAILS[0]}\n" + header += f"# Commit: {TOPOSTATS_COMMIT}\n" + output_config.write_text(header, encoding="utf-8") yaml = YAML(typ="safe") diff --git a/topostats/processing.py b/topostats/processing.py index 9615d053504..6046ae227e4 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -10,6 +10,7 @@ import pandas as pd from art import tprint +from topostats import TOPOSTATS_COMMIT, TOPOSTATS_VERSION from topostats.array_manipulation import re_crop_grain_image_and_mask_to_set_size_nm from topostats.filters import Filters from topostats.grains import GrainCrop, GrainCropsDirection, Grains, ImageGrainCrops @@ -1628,6 +1629,8 @@ def completion_message(config: dict, img_files: list, summary_config: dict, imag tprint("TopoStats", font="twisted") LOGGER.info( f"\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COMPLETE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n" + f" TopoStats Version : {TOPOSTATS_VERSION}\n" + f" TopoStats Commit : {TOPOSTATS_COMMIT}\n" f" Base Directory : {config['base_dir']}\n" f" File Extension : {config['file_ext']}\n" f" Files Found : {len(img_files)}\n" From a129951fa914c751b9689f1ae2c47cc099fefcf9 Mon Sep 17 00:00:00 2001 From: Neil Shephard Date: Thu, 14 Aug 2025 11:54:28 +0100 Subject: [PATCH 11/12] style: import TOPOSTATS_VERSION rather than TOPOSTATS_DETAILS Missed this in reviewing the code. We have already extracted the version from `TOPOSTATS_DETAILS` to `TOPOSTATS_VERSION` so may as well import it and do away with having to index the version from `TOPOSTATS_DETAILS`. --- topostats/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/topostats/io.py b/topostats/io.py index d1c43b1b582..0e8fb551bf8 100644 --- a/topostats/io.py +++ b/topostats/io.py @@ -21,7 +21,7 @@ from numpyencoder import NumpyEncoder from ruamel.yaml import YAML, YAMLError -from topostats import TOPOSTATS_COMMIT, TOPOSTATS_DETAILS, __release__, grains +from topostats import TOPOSTATS_COMMIT, TOPOSTATS_VERSION, __release__, grains from topostats.logs.logs import LOGGER_NAME LOGGER = logging.getLogger(LOGGER_NAME) @@ -239,7 +239,7 @@ def write_yaml( header = f"# Configuration from TopoStats run completed : {get_date_time()}\n" + CONFIG_DOCUMENTATION_REFERENCE # Add comment to config with topostats version + commit - header += f"# TopoStats version: {TOPOSTATS_DETAILS[0]}\n" + header += f"# TopoStats version: {TOPOSTATS_VERSION}\n" header += f"# Commit: {TOPOSTATS_COMMIT}\n" output_config.write_text(header, encoding="utf-8") From d24ff2c220aa6433f7b8a2faf1f279b5921f5753 Mon Sep 17 00:00:00 2001 From: Toby A Date: Wed, 27 Aug 2025 10:17:53 +0100 Subject: [PATCH 12/12] Increased number of try except statements in run_grainstats --- topostats/processing.py | 92 ++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/topostats/processing.py b/topostats/processing.py index 6046ae227e4..02313e6be57 100644 --- a/topostats/processing.py +++ b/topostats/processing.py @@ -1,7 +1,6 @@ """Functions for processing data.""" import logging -import sys from collections import defaultdict from pathlib import Path @@ -339,64 +338,73 @@ def run_grainstats( grainstats_config.pop("run") class_names = {index + 1: class_name for index, class_name in enumerate(grainstats_config.pop("class_names"))} # Grain Statistics : + LOGGER.info(f"[{filename}] : *** Grain Statistics ***") + grainstats_dict = {} + height_profiles_dict = {} try: - LOGGER.info(f"[{filename}] : *** Grain Statistics ***") grain_plot_dict = { key: value for key, value in plotting_config["plot_dict"].items() if key in ["grain_image", "grain_mask", "grain_mask_image"] } - grainstats_dict = {} - height_profiles_dict = {} + except Exception as e: + LOGGER.error( + f"[{filename}] : An error occurred whilst creating a grain plots dictionary: {e}\nReturning empty dataframe." + ) + return create_empty_dataframe(column_set="grainstats"), height_profiles_dict, {} - # There are two layers to process those above the given threshold and those below - grain_crops_direction: GrainCropsDirection + # There are two layers to process those above the given threshold and those below + grain_crops_direction: GrainCropsDirection + try: for direction, grain_crops_direction in image_grain_crops.__dict__.items(): if grain_crops_direction is None: LOGGER.warning( f"No grains exist for the {direction} direction. Skipping grainstats for {direction}." ) continue - grainstats_calculator = GrainStats( - grain_crops=grain_crops_direction.crops, - direction=direction, - base_output_dir=grain_out_path, - image_name=filename, - plot_opts=grain_plot_dict, - **grainstats_config, - ) - grainstats_dict[direction], height_profiles_dict[direction] = grainstats_calculator.calculate_stats() - grainstats_dict[direction]["threshold"] = direction - # Create results dataframe from above and below results - # Appease pylint and ensure that grainstats_df is always created - grainstats_df = create_empty_dataframe(column_set="grainstats") - if "above" in grainstats_dict and "below" in grainstats_dict: - grainstats_df = pd.concat([grainstats_dict["below"], grainstats_dict["above"]]) - elif "above" in grainstats_dict: - grainstats_df = grainstats_dict["above"] - elif "below" in grainstats_dict: - grainstats_df = grainstats_dict["below"] - else: - raise ValueError( - "grainstats dictionary has neither 'above' nor 'below' keys. This should be impossible." - ) - grainstats_df["basename"] = basename.parent - grainstats_df["class_name"] = grainstats_df["class_number"].map(class_names) - LOGGER.info(f"[{filename}] : Calculated grainstats for {len(grainstats_df)} grains.") - LOGGER.info(f"[{filename}] : Grainstats stage completed successfully.") - return grainstats_df, height_profiles_dict, grainstats_calculator.grain_crops + try: + grainstats_calculator = GrainStats( + grain_crops=grain_crops_direction.crops, + direction=direction, + base_output_dir=grain_out_path, + image_name=filename, + plot_opts=grain_plot_dict, + **grainstats_config, + ) + grainstats_dict[direction], height_profiles_dict[direction] = ( + grainstats_calculator.calculate_stats() + ) + grainstats_dict[direction]["threshold"] = direction + except Exception as e: + LOGGER.error( + f"[{filename}] : An error occurred whilst calculating grain statistics: {e}\nReturning empty dataframe." + ) + return create_empty_dataframe(column_set="grainstats"), height_profiles_dict, {} except Exception as e: - _, _, e_traceback = sys.exc_info() - e_line_no = e_traceback.tb_lineno LOGGER.error( - f"[{filename}] : An error occurred whilst calculating grain statistics on line {e_line_no}: {e}\nReturning empty dataframe." + f"[{filename}] : An error occurred whilst trying to iterate over directions: {e}\nReturning empty dataframe." ) return create_empty_dataframe(column_set="grainstats"), height_profiles_dict, {} - else: - LOGGER.info( - f"[{filename}] : Calculation of grainstats disabled, returning empty dataframe and empty height_profiles." - ) - return create_empty_dataframe(column_set="grainstats"), {}, {} + # Create results dataframe from above and below results + # Appease pylint and ensure that grainstats_df is always created + grainstats_df = create_empty_dataframe(column_set="grainstats") + if "above" in grainstats_dict and "below" in grainstats_dict: + grainstats_df = pd.concat([grainstats_dict["below"], grainstats_dict["above"]]) + elif "above" in grainstats_dict: + grainstats_df = grainstats_dict["above"] + elif "below" in grainstats_dict: + grainstats_df = grainstats_dict["below"] + else: + raise ValueError("grainstats dictionary has neither 'above' nor 'below' keys. This should be impossible.") + grainstats_df["basename"] = basename.parent + grainstats_df["class_name"] = grainstats_df["class_number"].map(class_names) + LOGGER.info(f"[{filename}] : Calculated grainstats for {len(grainstats_df)} grains.") + LOGGER.info(f"[{filename}] : Grainstats stage completed successfully.") + return grainstats_df, height_profiles_dict, grainstats_calculator.grain_crops + LOGGER.info( + f"[{filename}] : Calculation of grainstats disabled, returning empty dataframe and empty height_profiles." + ) + return create_empty_dataframe(column_set="grainstats"), {}, {} def run_disordered_tracing(