From a2fa403e1f9295d52b72954d39f09f3412f8e1a8 Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Fri, 10 Nov 2023 13:24:45 +0000 Subject: [PATCH 01/12] Replace print with loguru --- pyproject.toml | 2 +- tests/test_asd.py | 4 +--- topofileformats/asd.py | 38 +++++++++++++++++++------------------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e7872ea..923b783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ] -dependencies = ["numpy"] +dependencies = ["numpy", "loguru"] [project.optional-dependencies] dev = ["pytest", "black", "pylint"] diff --git a/tests/test_asd.py b/tests/test_asd.py index d8ac54d..9bb30d5 100644 --- a/tests/test_asd.py +++ b/tests/test_asd.py @@ -30,9 +30,7 @@ ), ], ) -def test_load_asd( - file_name: str, channel: str, number_of_frames: int, pixel_to_nm_scaling: float -) -> None: +def test_load_asd(file_name: str, channel: str, number_of_frames: int, pixel_to_nm_scaling: float) -> None: """Test the normal operation of loading a .asd file.""" result_frames = list diff --git a/topofileformats/asd.py b/topofileformats/asd.py index 069a25a..c0595cb 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -1,8 +1,10 @@ """For decoding and loading .asd AFM file format into Python Numpy arrays""" from pathlib import Path +import sys from typing import BinaryIO, Union +from loguru import logger import numpy as np import matplotlib.pyplot as plt from matplotlib import animation @@ -21,6 +23,9 @@ skip_bytes, ) +logger.remove() +logger.add(sys.stderr, format="{time} | {level} | {message}") + # pylint: disable=too-few-public-methods class VoltageLevelConverter: @@ -35,7 +40,7 @@ def __init__(self, analogue_digital_range, max_voltage, scaling_factor, resoluti self.max_voltage = max_voltage self.scaling_factor = scaling_factor self.resolution = resolution - print( + logger.info( f"created voltage converter. ad_range: {analogue_digital_range} -> {self.ad_range}, \ max voltage: {max_voltage}, scaling factor: {scaling_factor}, resolution: {resolution}" ) @@ -115,19 +120,19 @@ def calculate_scaling_factor( in the .asd file. """ if channel == "TP": - print( + logger.info( f"Scaling factor: Type: {channel} -> TP | piezo extension {z_piezo_gain} \ * piezo gain {z_piezo_extension} = scaling factor {z_piezo_gain * z_piezo_extension}" ) return z_piezo_gain * z_piezo_extension if channel == "ER": - print( + logger.info( f"Scaling factor: Type: {channel} -> ER | - scanner sensitivity {-scanner_sensitivity} \ = scaling factor {-scanner_sensitivity}" ) return -scanner_sensitivity if channel == "PH": - print( + logger.info( f"Scaling factor: Type: {channel} -> PH | - phase sensitivity {-phase_sensitivity} \ = scaling factor {-phase_sensitivity}" ) @@ -179,12 +184,12 @@ def load_asd(file_path: Union[Path, str], channel: str): f"File version {file_version} unknown. Please add support if you\ know how to decode this file version." ) - print(header_dict) + logger.info(f"header dict: \n{header_dict}") pixel_to_nanometre_scaling_factor_x = header_dict["x_nm"] / header_dict["x_pixels"] pixel_to_nanometre_scaling_factor_y = header_dict["y_nm"] / header_dict["y_pixels"] if pixel_to_nanometre_scaling_factor_x != pixel_to_nanometre_scaling_factor_y: - print( + logger.warning( f"WARNING: Resolution of image is different in x and y directions:\ x: {pixel_to_nanometre_scaling_factor_x}\ y: {pixel_to_nanometre_scaling_factor_y}" @@ -192,12 +197,12 @@ def load_asd(file_path: Union[Path, str], channel: str): pixel_to_nanometre_scaling_factor = pixel_to_nanometre_scaling_factor_x if channel == header_dict["channel1"]: - print( + logger.info( f"Requested channel {channel} matches first channel in file: \ {header_dict['channel1']}" ) elif channel == header_dict["channel2"]: - print( + logger.info( f"Requested channel {channel} matches second channel in file: \ {header_dict['channel2']}" ) @@ -206,12 +211,9 @@ def load_asd(file_path: Union[Path, str], channel: str): _size_of_frame_header = header_dict["frame_header_length"] # Remember that each value is two bytes (since signed int16) size_of_single_frame_plus_header = ( - header_dict["frame_header_length"] - + header_dict["x_pixels"] * header_dict["y_pixels"] * 2 - ) - length_of_all_first_channel_frames = ( - header_dict["num_frames"] * size_of_single_frame_plus_header + header_dict["frame_header_length"] + header_dict["x_pixels"] * header_dict["y_pixels"] * 2 ) + length_of_all_first_channel_frames = header_dict["num_frames"] * size_of_single_frame_plus_header _ = open_file.read(length_of_all_first_channel_frames) else: raise ValueError( @@ -259,7 +261,7 @@ def read_file_version(open_file): Integer file version decoded from file. """ file_version = read_int32(open_file) - print(f"file version: {file_version}") + logger.info(f"file version: {file_version}") return file_version @@ -345,9 +347,7 @@ def read_header_file_version_0(open_file: BinaryIO): # ID of the file header_dict["file_id"] = read_int16(open_file) # Name of the user - header_dict["user_name"] = read_null_separated_utf8( - open_file, length_bytes=header_dict["user_name_size"] - ) + header_dict["user_name"] = read_null_separated_utf8(open_file, length_bytes=header_dict["user_name_size"]) # Sensitivity of the scanner in nm / V header_dict["scanner_sensitivity"] = read_float(open_file) # Phase sensitivity @@ -759,8 +759,8 @@ def create_analogue_digital_converter(analogue_digital_range, scaling_factor, re f"Analogue to digital range hex value {analogue_digital_range} has no known \ analogue-digital mapping." ) - print(f"Analogue to digital mapping | Range: {analogue_digital_range} -> {mapping}") - print(f"Converter: {converter}") + logger.info(f"Analogue to digital mapping | Range: {analogue_digital_range} -> {mapping}") + logger.info(f"Converter: {converter}") return converter From 4b7e71e0a2811ccf336162a4a1196acf88d4c14d Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 12:49:42 +0000 Subject: [PATCH 02/12] Fix dependency duplication --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 41a7dc1..354937b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ] -dependencies = ["numpy", "loguru"] + keywords = [ "afm", "image processing" @@ -26,7 +26,8 @@ keywords = [ dependencies = [ "matplotlib", - "numpy" + "numpy", + "loguru", ] [project.optional-dependencies] From 27e04ad9f2bb4f2a45e9497e0687685bd055a24d Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 14:04:46 +0000 Subject: [PATCH 03/12] Fix superfluous encoding parameter for binary file opening --- topofileformats/asd.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index ada3f6e..28c0b98 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -1,8 +1,9 @@ """For decoding and loading .asd AFM file format into Python Numpy arrays.""" +from __future__ import annotations from pathlib import Path import sys -from typing import BinaryIO, Union +from typing import BinaryIO from loguru import logger import numpy as np @@ -168,7 +169,9 @@ def load_asd(file_path: Path | str, channel: str): version please either look into the `read_header_file_version_x` functions or print the keys too see what metadata is available. """ - with Path.open(file_path, "rb", encoding="UTF-8") as open_file: + # Binary mode open does not take an encoding argument + # pylint: disable=unspecified-encoding + with Path.open(file_path, "rb") as open_file: file_version = read_file_version(open_file) if file_version == 0: From c66e8bd060b85fd1e7b96e4b452602203df22491 Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 14:25:49 +0000 Subject: [PATCH 04/12] Move logging setup to dedicated file --- topofileformats/asd.py | 13 ++++++------- topofileformats/logging.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 topofileformats/logging.py diff --git a/topofileformats/asd.py b/topofileformats/asd.py index 28c0b98..4d8fa47 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -2,14 +2,16 @@ from __future__ import annotations from pathlib import Path -import sys + from typing import BinaryIO -from loguru import logger + import numpy as np import matplotlib.pyplot as plt from matplotlib import animation + +from topofileformats.logging import logger from topofileformats.io import ( read_int32, read_int16, @@ -24,9 +26,6 @@ skip_bytes, ) -logger.remove() -logger.add(sys.stderr, format="{time} | {level} | {message}") - # pylint: disable=too-few-public-methods class VoltageLevelConverter: @@ -144,12 +143,12 @@ def calculate_scaling_factor( raise ValueError(f"channel {channel} not known for .asd file type.") -def load_asd(file_path: Path | str, channel: str): +def load_asd(file_path: Path, channel: str): """Load a .asd file. Parameters ---------- - file_path: Union[Path, str] + file_path: Path Path to the .asd file. channel: str Channel to load. Note that only three channels seem to be present in a single .asd file. Options: TP diff --git a/topofileformats/logging.py b/topofileformats/logging.py new file mode 100644 index 0000000..c730233 --- /dev/null +++ b/topofileformats/logging.py @@ -0,0 +1,14 @@ +"""Configure logging.""" + +import sys + +from loguru import logger + +logger.remove() +# Set the format to have blue time, green file, module, function and line, and white message +logger.add( + sys.stderr, + colorize=True, + format="{time:HH:mm:ss} | {file}:{module}:" + "{function}:{line} | {message}", +) From 6d5a80776502018def3eff12f69e9c36ca12404b Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 14:27:10 +0000 Subject: [PATCH 05/12] Fix path --- examples/example_01.ipynb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/example_01.ipynb b/examples/example_01.ipynb index bd25239..a39aee1 100644 --- a/examples/example_01.ipynb +++ b/examples/example_01.ipynb @@ -6,6 +6,8 @@ "metadata": {}, "outputs": [], "source": [ + "from pathlib import Path\n", + "\n", "from topofileformats.asd import load_asd, create_animation" ] }, @@ -15,7 +17,7 @@ "metadata": {}, "outputs": [], "source": [ - "FILE = \"../tests/resources/sample_0.asd\"\n", + "FILE = Path(\"../tests/resources/sample_0.asd\")\n", "frames, pixel_to_nm_scaling, metadata = load_asd(file_path=FILE, channel=\"TP\")" ] }, From d0ea520c5ac4bdf8e99a0ea4d165973846e07145 Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 14:29:20 +0000 Subject: [PATCH 06/12] Remove redundant warning --- topofileformats/asd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index 4d8fa47..dc623df 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -192,7 +192,7 @@ def load_asd(file_path: Path, channel: str): pixel_to_nanometre_scaling_factor_y = header_dict["y_nm"] / header_dict["y_pixels"] if pixel_to_nanometre_scaling_factor_x != pixel_to_nanometre_scaling_factor_y: logger.warning( - f"WARNING: Resolution of image is different in x and y directions:\ + f"Resolution of image is different in x and y directions:\ x: {pixel_to_nanometre_scaling_factor_x}\ y: {pixel_to_nanometre_scaling_factor_y}" ) From a3a94da939bfe16b6ea49659ca24392567b4fec3 Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 16:10:02 +0000 Subject: [PATCH 07/12] Improve long string wrapping, thanks @ns-rse --- topofileformats/asd.py | 43 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index dc623df..7cf82a3 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -42,8 +42,8 @@ def __init__(self, analogue_digital_range, max_voltage, scaling_factor, resoluti self.scaling_factor = scaling_factor self.resolution = resolution logger.info( - f"created voltage converter. ad_range: {analogue_digital_range} -> {self.ad_range}, \ -max voltage: {max_voltage}, scaling factor: {scaling_factor}, resolution: {resolution}" + f"created voltage converter. ad_range: {analogue_digital_range} -> {self.ad_range}, " + f"max voltage: {max_voltage}, scaling factor: {scaling_factor}, resolution: {resolution}" ) @@ -123,20 +123,20 @@ def calculate_scaling_factor( """ if channel == "TP": logger.info( - f"Scaling factor: Type: {channel} -> TP | piezo extension {z_piezo_gain} \ -* piezo gain {z_piezo_extension} = scaling factor {z_piezo_gain * z_piezo_extension}" + f"Scaling factor: Type: {channel} -> TP | piezo extension {z_piezo_gain} " + f"* piezo gain {z_piezo_extension} = scaling factor {z_piezo_gain * z_piezo_extension}" ) return z_piezo_gain * z_piezo_extension if channel == "ER": logger.info( - f"Scaling factor: Type: {channel} -> ER | - scanner sensitivity {-scanner_sensitivity} \ -= scaling factor {-scanner_sensitivity}" + f"Scaling factor: Type: {channel} -> ER | - scanner sensitivity {-scanner_sensitivity} " + f"= scaling factor {-scanner_sensitivity}" ) return -scanner_sensitivity if channel == "PH": logger.info( - f"Scaling factor: Type: {channel} -> PH | - phase sensitivity {-phase_sensitivity} \ -= scaling factor {-phase_sensitivity}" + f"Scaling factor: Type: {channel} -> PH | - phase sensitivity {-phase_sensitivity} " + f"= scaling factor {-phase_sensitivity}" ) return -phase_sensitivity @@ -183,8 +183,8 @@ def load_asd(file_path: Path, channel: str): header_dict = read_header_file_version_2(open_file) else: raise ValueError( - f"File version {file_version} unknown. Please add support if you\ -know how to decode this file version." + f"File version {file_version} unknown. Please add support if you " + "know how to decode this file version." ) logger.info(f"header dict: \n{header_dict}") @@ -192,22 +192,16 @@ def load_asd(file_path: Path, channel: str): pixel_to_nanometre_scaling_factor_y = header_dict["y_nm"] / header_dict["y_pixels"] if pixel_to_nanometre_scaling_factor_x != pixel_to_nanometre_scaling_factor_y: logger.warning( - f"Resolution of image is different in x and y directions:\ -x: {pixel_to_nanometre_scaling_factor_x}\ - y: {pixel_to_nanometre_scaling_factor_y}" + f"Resolution of image is different in x and y directions:" + f"x: {pixel_to_nanometre_scaling_factor_x}" + f"y: {pixel_to_nanometre_scaling_factor_y}" ) pixel_to_nanometre_scaling_factor = pixel_to_nanometre_scaling_factor_x if channel == header_dict["channel1"]: - logger.info( - f"Requested channel {channel} matches first channel in file: \ -{header_dict['channel1']}" - ) + logger.info(f"Requested channel {channel} matches first channel in file: " f"{header_dict['channel1']}") elif channel == header_dict["channel2"]: - logger.info( - f"Requested channel {channel} matches second channel in file: \ -{header_dict['channel2']}" - ) + logger.info(f"Requested channel {channel} matches second channel in file: " f"{header_dict['channel2']}") # Skip first channel data _size_of_frame_header = header_dict["frame_header_length"] @@ -219,8 +213,8 @@ def load_asd(file_path: Path, channel: str): _ = open_file.read(length_of_all_first_channel_frames) else: raise ValueError( - f"Channel {channel} not found in this file's available channels: \ -{header_dict['channel1']}, {header_dict['channel2']}" + f"Channel {channel} not found in this file's available channels: " + f"{header_dict['channel1']}, {header_dict['channel2']}" ) scaling_factor = calculate_scaling_factor( @@ -756,8 +750,7 @@ def create_analogue_digital_converter(analogue_digital_range, scaling_factor, re ) else: raise ValueError( - f"Analogue to digital range hex value {analogue_digital_range} has no known \ -analogue-digital mapping." + f"Analogue to digital range hex value {analogue_digital_range} has no known " "analogue-digital mapping." ) logger.info(f"Analogue to digital mapping | Range: {analogue_digital_range} -> {mapping}") logger.info(f"Converter: {converter}") From a465a26b638dfe18454c26b6c7bc0932b4193b2f Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 16:30:45 +0000 Subject: [PATCH 08/12] Switch to debug log level for asd header --- topofileformats/asd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index 7cf82a3..92ae939 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -186,7 +186,7 @@ def load_asd(file_path: Path, channel: str): f"File version {file_version} unknown. Please add support if you " "know how to decode this file version." ) - logger.info(f"header dict: \n{header_dict}") + logger.debug(f"header dict: \n{header_dict}") pixel_to_nanometre_scaling_factor_x = header_dict["x_nm"] / header_dict["x_pixels"] pixel_to_nanometre_scaling_factor_y = header_dict["y_nm"] / header_dict["y_pixels"] From 404c26aaa879982cb8a26db31b23a3c92cf085ff Mon Sep 17 00:00:00 2001 From: SylviaWhittle Date: Thu, 23 Nov 2023 16:38:20 +0000 Subject: [PATCH 09/12] Improve logging format --- topofileformats/logging.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/topofileformats/logging.py b/topofileformats/logging.py index c730233..5d3a70f 100644 --- a/topofileformats/logging.py +++ b/topofileformats/logging.py @@ -9,6 +9,7 @@ logger.add( sys.stderr, colorize=True, - format="{time:HH:mm:ss} | {file}:{module}:" - "{function}:{line} | {message}", + format="{time:HH:mm:ss} | {level} |" + "{file}:{module}:" + "{function}:{line} | {message}", ) From afb19e8394e1c40ff7b6dbaf7a51324421f5e5af Mon Sep 17 00:00:00 2001 From: Sylvia Whittle <86117496+SylviaWhittle@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:37:13 +0000 Subject: [PATCH 10/12] Remove unnecessary line break Co-authored-by: Neil Shephard --- topofileformats/asd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index 92ae939..b8640b7 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -199,7 +199,7 @@ def load_asd(file_path: Path, channel: str): pixel_to_nanometre_scaling_factor = pixel_to_nanometre_scaling_factor_x if channel == header_dict["channel1"]: - logger.info(f"Requested channel {channel} matches first channel in file: " f"{header_dict['channel1']}") + logger.info(f"Requested channel {channel} matches first channel in file: {header_dict['channel1']}") elif channel == header_dict["channel2"]: logger.info(f"Requested channel {channel} matches second channel in file: " f"{header_dict['channel2']}") From 9b2f76fe0f76c424abd8b0aa7f0a8cc17299f887 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:38:22 +0000 Subject: [PATCH 11/12] [pre-commit.ci] Fixing issues with pre-commit --- topofileformats/asd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index c4053a0..b8640b7 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -168,11 +168,9 @@ def load_asd(file_path: Path, channel: str): version please either look into the `read_header_file_version_x` functions or print the keys too see what metadata is available. """ - # Binary mode open does not take an encoding argument # pylint: disable=unspecified-encoding with Path.open(file_path, "rb") as open_file: - file_version = read_file_version(open_file) if file_version == 0: From e1b13926aa5e330761daf68fd353fae2a43072a2 Mon Sep 17 00:00:00 2001 From: Neil Shephard Date: Fri, 26 Apr 2024 14:05:08 +0100 Subject: [PATCH 12/12] chore: Rounding out logging Did some [reading](https://loguru.readthedocs.io/en/stable/resources/recipes.html#configuring-loguru-to-be-used-by-a-library-or-an-application) and I was wrong to suggest setting up `loguru` in `__init__.py` so happy to use `topofileforamts/logging.py`. Implemented the one recommendation in the above linked section. No tests for logging but logs _are_ output. --- .gitignore | 3 +++ topofileformats/__init__.py | 7 ++++++- topofileformats/asd.py | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cfc1042..52022f2 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,9 @@ dmypy.json # Pyre type checker .pyre/ +# Package version +_version.py + # VSCode .vscode/ diff --git a/topofileformats/__init__.py b/topofileformats/__init__.py index eedcb4f..47de769 100644 --- a/topofileformats/__init__.py +++ b/topofileformats/__init__.py @@ -1 +1,6 @@ -"""topofileformats.""" +"""A module for loading AFM files of different formats.""" + + +from loguru import logger + +logger.disable(__package__) diff --git a/topofileformats/asd.py b/topofileformats/asd.py index b8640b7..5640b59 100644 --- a/topofileformats/asd.py +++ b/topofileformats/asd.py @@ -26,6 +26,8 @@ skip_bytes, ) +logger.enable(__package__) + # pylint: disable=too-few-public-methods class VoltageLevelConverter: