Repository: ruancomelli/boiling-learning
Pull request #226: ci: replace old pre-commit hooks with local format, lint and typecheck
Author: login: ruancomelli
Labels: name: size/L
Created: 2025-04-01T15:50:00Z | Merged: 2025-04-01T15:58:54Z

## Summary by Sourcery

Replace old pre-commit hooks with local format, lint, and typecheck scripts

CI:
- Replace remote pre-commit hooks with local scripts for formatting, linting, and typechecking
- Update pre-commit configuration to use local script-based hooks instead of external tools

Chores:
- Standardize code formatting and linting workflow using local scripts
- Update various configuration files to improve code quality checks


Diff:
diff --git a/.github/actions/setup-python-environment/action.yaml b/.github/actions/setup-python-environment/action.yaml
index aadecd67..fb2fee32 100644
--- a/.github/actions/setup-python-environment/action.yaml
+++ b/.github/actions/setup-python-environment/action.yaml
@@ -3,22 +3,22 @@ description: Setup Python and install project dependencies
 
 inputs:
   python-version:
-    default: "3.10"
+    default: '3.10'
     description: Python version number to use.
     required: true
 
 runs:
   using: composite
   steps:
-    - name: Checkout code
-      uses: actions/checkout@v4
+  - name: Checkout code
+    uses: actions/checkout@v4
 
-    - name: Set up Python ${{ inputs.python-version }}
-      uses: astral-sh/setup-uv@v5
-      with:
-        enable-cache: true
-        python-version: ${{ inputs.python-version }}
+  - name: Set up Python ${{ inputs.python-version }}
+    uses: astral-sh/setup-uv@v5
+    with:
+      enable-cache: true
+      python-version: ${{ inputs.python-version }}
 
-    - name: Install project and dependencies
-      shell: bash
-      run: uv sync
+  - name: Install project and dependencies
+    shell: bash
+    run: uv sync
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index fccb6d1c..74ed1252 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -13,28 +13,28 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.10"]
+        python-version: ['3.10']
         check:
-          - name: lint
-            run: make lint
-          - name: format
-            run: make format
+        - name: lint
+          run: make lint
+        - name: format
+          run: make format
           # TODO: re-enable typecheck after fixing typechecking errors
           # - name: typecheck
           #   run: make typecheck
-          - name: test
-            run: make test
+        - name: test
+          run: make test
           # TODO: re-enable test-cov after fixing test coverage errors
           # - name: test-cov
           #   run: make test-cov
     steps:
-      - uses: actions/checkout@v4
+    - uses: actions/checkout@v4
 
-      - name: Set up Python ${{ matrix.python-version }}
-        id: setup-python
-        uses: ./.github/actions/setup-python-environment
-        with:
-          python-version: ${{ matrix.python-version }}
+    - name: Set up Python ${{ matrix.python-version }}
+      id: setup-python
+      uses: ./.github/actions/setup-python-environment
+      with:
+        python-version: ${{ matrix.python-version }}
 
-      - name: Run check '${{ matrix.check.name }}'
-        run: ${{ matrix.check.run }}
+    - name: Run check '${{ matrix.check.name }}'
+      run: ${{ matrix.check.run }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ba5ec268..f8c46fd2 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -2,7 +2,7 @@ name: Release package
 on:
   push:
     tags:
-      - v* # Push events to matching v*, i.e. v1.0, v20.15.10
+    - v*   # Push events to matching v*, i.e. v1.0, v20.15.10
 
 jobs:
   build:
@@ -12,14 +12,14 @@ jobs:
     if: "!contains(github.event.head_commit.message, 'release-skip')"
 
     steps:
-      - uses: actions/checkout@v4
+    - uses: actions/checkout@v4
 
-      - name: Set up Python
-        id: setup-python
-        uses: ./.github/actions/setup-python-environment
+    - name: Set up Python
+      id: setup-python
+      uses: ./.github/actions/setup-python-environment
 
-      - name: Run tests
-        run: make test
+    - name: Run tests
+      run: make test
 
   release:
     name: Create release
@@ -28,31 +28,31 @@ jobs:
     needs: build
 
     steps:
-      - name: Checkout code
-        uses: actions/checkout@v4
-
-      - name: Zip boiling_learning/
-        run: |
-          zip -r boiling_learning.zip boiling_learning
-      - name: Create Release
-        id: create_release
-        uses: actions/create-release@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is auto provided by Actions
-        with:
-          tag_name: ${{ github.ref }} # get tag
-          release_name: Release ${{ github.ref }}
-          body: ${{ github.event.head_commit.message }} # get commit message
-          draft: false
-          prerelease: false
-
-      - name: Upload Release Asset
-        id: upload-release-asset
-        uses: actions/upload-release-asset@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the step above
-          asset_path: ./boiling_learning.zip
-          asset_name: boiling_learning.zip
-          asset_content_type: application/zip
+    - name: Checkout code
+      uses: actions/checkout@v4
+
+    - name: Zip boiling_learning/
+      run: |
+        zip -r boiling_learning.zip boiling_learning
+    - name: Create Release
+      id: create_release
+      uses: actions/create-release@v1
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}   # This token is auto provided by Actions
+      with:
+        tag_name: ${{ github.ref }}   # get tag
+        release_name: Release ${{ github.ref }}
+        body: ${{ github.event.head_commit.message }}   # get commit message
+        draft: false
+        prerelease: false
+
+    - name: Upload Release Asset
+      id: upload-release-asset
+      uses: actions/upload-release-asset@v1
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      with:
+        upload_url: ${{ steps.create_release.outputs.upload_url }}   # This pulls from the step above
+        asset_path: ./boiling_learning.zip
+        asset_name: boiling_learning.zip
+        asset_content_type: application/zip
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9275ec4f..0e8a6b8a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,4 +1,12 @@
 repos:
+- repo: https://github.com/astral-sh/uv-pre-commit
+  rev: 0.6.11
+  hooks:
+      # Update the uv lockfile
+  - id: uv-lock
+        # Install all packages
+  - id: uv-sync
+    args: [--locked, --all-packages]
 
 - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update
   rev: v0.6.1
@@ -29,34 +37,31 @@ repos:
     args: [--autofix, --indent, '4', --no-sort]
 
 - repo: https://github.com/compilerla/conventional-pre-commit
-  rev: v2.1.1
+  rev: v4.0.0
   hooks:
   - id: conventional-pre-commit
     stages: [commit-msg]
 
-- repo: https://github.com/hakancelikdev/unimport
-  rev: 33ead41ee30f1d323a9c2fcfd0114297efbbc4d5
+- repo: local
   hooks:
-  - id: unimport
-    args: [--check, --gitignore, --ignore-init, --include-star-import, boiling_learning, tests]
 
-- repo: https://github.com/psf/black
-  rev: 23.3.0
-  hooks:
-  - id: black
-    args: [--config, pyproject.toml]
+  - id: format
+    name: Format Python code
+    stages: [pre-commit, pre-push]
+    language: system
+    entry: bash scripts/format.sh
+    types: [python]
 
-- repo: https://github.com/pycqa/isort
-  rev: 5.12.0
-  hooks:
-  - id: isort
-    name: isort (python)
-    args: [--check]
-  - id: isort
-    name: isort (cython)
-    types: [cython]
-    args: [--check]
-  - id: isort
-    name: isort (pyi)
-    types: [pyi]
-    args: [--check]
\ No newline at end of file
+  - id: lint
+    name: Lint Python code and fix violations
+    stages: [pre-commit, pre-push]
+    language: system
+    entry: bash scripts/lint.sh
+    types: [python]
+
+  - id: typecheck
+    name: Typecheck Python code
+    stages: [pre-commit, pre-push]
+    language: system
+    entry: bash scripts/typecheck.sh
+    types: [python]
diff --git a/pyproject.toml b/pyproject.toml
index dc057e5a..c2d3a39a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,8 +2,7 @@
 dynamic = ["version"]
 name = "boiling-learning"
 description = "Deep learning models for phase-change quantification and classification"
-authors = [{ name = "Ruan Comelli", email = "ruancomelli@gmail.com" }]
-
+authors = [{name = "Ruan Comelli", email = "ruancomelli@gmail.com"}]
 license = "GPL-3.0-only"
 license-files = ["LICENSE"]
 readme = "README.md"
@@ -14,7 +13,7 @@ keywords = [
     "machine-learning",
     "phase-change",
     "python",
-    "tensorflow",
+    "tensorflow"
 ]
 classifiers = [
     "Development Status :: 3 - Alpha",
@@ -26,9 +25,8 @@ classifiers = [
     "Topic :: Scientific/Engineering",
     "Topic :: Scientific/Engineering :: Image Processing",
     "Topic :: Scientific/Engineering :: Image Recognition",
-    "Topic :: Scientific/Engineering :: Physics",
+    "Topic :: Scientific/Engineering :: Physics"
 ]
-
 requires-python = "==3.10.*"
 dependencies = [
     "tensorflow==2.10.*",
@@ -59,7 +57,7 @@ dependencies = [
     "pandas==2.0.*",
     "h5py==3.8.*",
     "uncertainties==3.1.*",
-    "pyyaml==6.*",
+    "pyyaml==6.*"
 ]
 
 [project.urls]
@@ -78,9 +76,7 @@ test-cov = ["pytest>=8.3.4", "pytest-cov>=6.0.0"]
 typecheck = [
     "mypy>=1.15.0",
     "types-decorator>=5.1.8",
-    "types-PyYAML>=6.0.11",
-    # TODO: support funcy>=2 in funcy-stubs, and then re-add it:
-    # "funcy-stubs>=0.1.1",
+    "types-PyYAML>=6.0.11"
 ]
 vulture = ["vulture>=2.7"]
 
@@ -113,7 +109,7 @@ warn_unused_configs = true
 warn_unused_ignores = true
 plugins = [
     # "classes.contrib.mypy.classes_plugin",
-    "numpy.typing.mypy_plugin",
+    "numpy.typing.mypy_plugin"
 ]
 
 [tool.commitizen]
@@ -129,36 +125,36 @@ testpaths = ["boiling_learning", "tests"]
 
 [tool.ruff.lint]
 select = [
-    "D",      # pydocstyle
-    "F",      # pyflakes
-    "I",      # isort
-    "PL",     # pylint
-    "RUF100", # unused-noqa-directive
-    "UP",     # pyupgrade
+    "D",  # pydocstyle
+    "F",  # pyflakes
+    "I",  # isort
+    "PL",  # pylint
+    "RUF100",  # unused-noqa-directive
+    "UP"  # pyupgrade
 ]
 ignore = [
-    "D100",    # Missing docstring in public module
-    "D101",    # Missing docstring in public class
-    "D102",    # Missing docstring in public method
-    "D103",    # Missing docstring in public function
-    "D104",    # Missing docstring in public package
-    "D105",    # Missing docstring in magic method
-    "D106",    # Missing docstring in public nested class
-    "D107",    # Missing docstring in __init__
-    "D203",    # incorrect-blank-line-before-class
-    "D213",    # multi-line-summary-second-line
-    "D413",    # Missing blank line after last section
-    "PLR0912", # Too many branches
-    "PLR0913", # Too many arguments in function definition
+    "D100",  # Missing docstring in public module
+    "D101",  # Missing docstring in public class
+    "D102",  # Missing docstring in public method
+    "D103",  # Missing docstring in public function
+    "D104",  # Missing docstring in public package
+    "D105",  # Missing docstring in magic method
+    "D106",  # Missing docstring in public nested class
+    "D107",  # Missing docstring in __init__
+    "D203",  # incorrect-blank-line-before-class
+    "D213",  # multi-line-summary-second-line
+    "D413",  # Missing blank line after last section
+    "PLR0912",  # Too many branches
+    "PLR0913"  # Too many arguments in function definition
 ]
 
 [tool.ruff.lint.per-file-ignores]
 "__init__.py" = [
-    "F401", # Unused imports
+    "F401"  # Unused imports
 ]
 "tests/**/*.py" = [
-    "D",       # Documentation
-    "PLR2004", # Magic value used in comparison
+    "D",  # Documentation
+    "PLR2004"  # Magic value used in comparison
 ]
 
 [tool.ruff.lint.pydocstyle]
diff --git a/resources/experimental-set-calibration/Processing/calibrate.py b/resources/experimental-set-calibration/Processing/calibrate.py
index 417ec6ba..99643d94 100644
--- a/resources/experimental-set-calibration/Processing/calibrate.py
+++ b/resources/experimental-set-calibration/Processing/calibrate.py
@@ -11,15 +11,15 @@
 
 # Support functions
 def regularized_untangled_file_name(details):
-    details['day'] = int(details['day'])
-    details['month'] = int(details['month'])
-    details['year'] = int(details['year'])
-    details['hour'] = int(details['hour'])
-    details['minute'] = int(details['minute'])
-    details['temperature_unity'] = float(details['temperature_unity'])
-    details['temperature_decy'] = float(details['temperature_decy'])
-    details['reference_temperature'] = (
-        details['temperature_unity'] + 0.01 * details['temperature_decy']
+    details["day"] = int(details["day"])
+    details["month"] = int(details["month"])
+    details["year"] = int(details["year"])
+    details["hour"] = int(details["hour"])
+    details["minute"] = int(details["minute"])
+    details["temperature_unity"] = float(details["temperature_unity"])
+    details["temperature_decy"] = float(details["temperature_decy"])
+    details["reference_temperature"] = (
+        details["temperature_unity"] + 0.01 * details["temperature_decy"]
     )
 
     return details
@@ -44,29 +44,29 @@ def untangle_file_name(file_name):
     return regularized_untangled_file_name(groupdict)
 
 
-def to_float(key, decimal_separator='.'):
-    return float(key.replace(decimal_separator, '.'))
+def to_float(key, decimal_separator="."):
+    return float(key.replace(decimal_separator, "."))
 
 
-def represents_float(key, decimal_separator='.'):
+def represents_float(key, decimal_separator="."):
     try:
-        float(key.replace(decimal_separator, '.'))
+        float(key.replace(decimal_separator, "."))
         return True
     except ValueError:
         return False
 
 
-def represents_float_list(keys, decimal_separator='.'):
+def represents_float_list(keys, decimal_separator="."):
     return all(represents_float(key, decimal_separator) for key in keys)
 
 
 def split_file(spamreader, decimal_separator):
-    file_data = {'data': [], 'header': []}
+    file_data = {"data": [], "header": []}
     for row in spamreader:
         if represents_float_list(row, decimal_separator=decimal_separator):
-            file_data['data'].append(row)
+            file_data["data"].append(row)
         else:
-            file_data['header'].append(row)
+            file_data["header"].append(row)
     return file_data
 
 
@@ -74,8 +74,8 @@ def split_file(spamreader, decimal_separator):
 
 
 # Details
-decimal_separator = ','
-data_path = pathlib.Path(__file__).parent.parent / 'Calibration' / 'Calibration Data'
+decimal_separator = ","
+data_path = pathlib.Path(__file__).parent.parent / "Calibration" / "Calibration Data"
 
 # Containers
 reference_temperature = {}
@@ -90,18 +90,22 @@ def split_file(spamreader, decimal_separator):
 
     # Reference temperature
     details = untangle_file_name(file_name)
-    reference_temperature[file_name] = float(details['reference_temperature'])
+    reference_temperature[file_name] = float(details["reference_temperature"])
 
     # Temperature data
     temperature_data[file_name] = []
     with open(data_file) as csvfile:
-        spamreader = csv.reader(csvfile, delimiter='\t')
-        file_data[file_name] = split_file(spamreader, decimal_separator=decimal_separator)
-        for string_temperature_pair in file_data[file_name]['data']:
+        spamreader = csv.reader(csvfile, delimiter="\t")
+        file_data[file_name] = split_file(
+            spamreader, decimal_separator=decimal_separator
+        )
+        for string_temperature_pair in file_data[file_name]["data"]:
             time_instant = to_float(
                 string_temperature_pair[0], decimal_separator=decimal_separator
             )
-            temperature = to_float(string_temperature_pair[1], decimal_separator=decimal_separator)
+            temperature = to_float(
+                string_temperature_pair[1], decimal_separator=decimal_separator
+            )
             temperature_data[file_name].append(temperature)
     measured_temperature[file_name] = np.mean(np.array(temperature_data[file_name]))
     temperature_pairs[file_name] = (
@@ -120,8 +124,8 @@ def split_file(spamreader, decimal_separator):
 def fit_polynomial(x, y, degree):
     model = Pipeline(
         [
-            ('poly', PolynomialFeatures(degree=degree)),
-            ('linear', LinearRegression(fit_intercept=False)),
+            ("poly", PolynomialFeatures(degree=degree)),
+            ("linear", LinearRegression(fit_intercept=False)),
         ]
     )
     model.fit(x[:, np.newaxis], y)
@@ -129,7 +133,7 @@ def fit_polynomial(x, y, degree):
 
 
 model = fit_polynomial(x, y, 3)
-coefficients = model.named_steps['linear'].coef_
+coefficients = model.named_steps["linear"].coef_
 
-with open(pathlib.Path(__file__).parent / 'coefficients.csv', 'w') as coeff_file:
-    coeff_file.writelines('%s\n' % item for item in coefficients)
+with open(pathlib.Path(__file__).parent / "coefficients.csv", "w") as coeff_file:
+    coeff_file.writelines("%s\n" % item for item in coefficients)
diff --git a/scripts/test-cov.sh b/scripts/test-cov.sh
index adc61270..7485cc10 100644
--- a/scripts/test-cov.sh
+++ b/scripts/test-cov.sh
@@ -5,4 +5,4 @@ uv run --group test-cov pytest \
     --cov-report=term \
     --cov-report=xml \
     --cov-fail-under=100 \
-    "$@"
\ No newline at end of file
+    "$@"
diff --git a/scripts/vulture.sh b/scripts/vulture.sh
index 8e5000ea..9f9130b6 100644
--- a/scripts/vulture.sh
+++ b/scripts/vulture.sh
@@ -4,4 +4,4 @@ uv run --group vulture \
 	vulture \
 	--ignore-decorators @*.dispatch*,@*.instance* \
 	--ignore-names __*[!_][!_] \
-	"$@"
\ No newline at end of file
+	"$@"


Reviews:
user: login: sourcery-ai[bot]
body: Hey @ruancomelli - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider using a tool like `sed` to perform the in-place file modifications in your format, lint, and typecheck scripts.
- It might be helpful to add a `make install` command to ensure the project is installed before running the tests.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟢 **General issues**: all looks good
- 🟢 **Security**: all looks good
- 🟡 **Review instructions**: 5 issues found
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T15:50:40Z

Review comments:
diff_hunk: @@ -11,15 +11,15 @@
 
 # Support functions
 def regularized_untangled_file_name(details):
-    details['day'] = int(details['day'])
-    details['month'] = int(details['month'])
-    details['year'] = int(details['year'])
-    details['hour'] = int(details['hour'])
-    details['minute'] = int(details['minute'])
-    details['temperature_unity'] = float(details['temperature_unity'])
-    details['temperature_decy'] = float(details['temperature_decy'])
-    details['reference_temperature'] = (
-        details['temperature_unity'] + 0.01 * details['temperature_decy']
+    details["day"] = int(details["day"])
path: resources/experimental-set-calibration/Processing/calibrate.py
user: login: sourcery-ai[bot]
body: **suggestion (review_instructions):** Consider adding a check for the presence of required keys in the details dictionary to avoid potential KeyErrors.

The function `regularized_untangled_file_name` assumes all keys exist in the details dictionary. It would be safer to check if these keys exist before accessing them, especially since this appears to be processing external data.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.py`

**Instructions:**
Check for the presence of required keys in JSON objects to avoid KeyErrors.

</details>
created_at: 2025-04-01T15:50:39Z
author_association: NONE
diff_hunk: @@ -90,18 +90,22 @@ def split_file(spamreader, decimal_separator):
 
     # Reference temperature
     details = untangle_file_name(file_name)
-    reference_temperature[file_name] = float(details['reference_temperature'])
+    reference_temperature[file_name] = float(details["reference_temperature"])
path: resources/experimental-set-calibration/Processing/calibrate.py
user: login: sourcery-ai[bot]
body: **suggestion (review_instructions):** Consider wrapping this critical data processing section in a try-except block to handle potential exceptions.

This code is parsing and processing external data files, which could lead to unexpected errors. Adding try-except blocks would make the code more robust and allow for proper error logging.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.py`

**Instructions:**
Wrap critical sections in try-except blocks to log exceptions to Sentry.

</details>
created_at: 2025-04-01T15:50:39Z
author_association: NONE
diff_hunk: @@ -120,16 +124,16 @@ def split_file(spamreader, decimal_separator):
 def fit_polynomial(x, y, degree):
path: resources/experimental-set-calibration/Processing/calibrate.py
user: login: sourcery-ai[bot]
body: **suggestion (review_instructions):** This function is missing a docstring to explain its purpose, parameters, and return value.

Adding a docstring would improve code readability and help other developers understand how to use this function correctly.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.py`

**Instructions:**
Add docstrings to functions and methods to explain their purpose and usage.

</details>
created_at: 2025-04-01T15:50:39Z
author_association: NONE
diff_hunk: @@ -11,15 +11,15 @@
 
 # Support functions
 def regularized_untangled_file_name(details):
path: resources/experimental-set-calibration/Processing/calibrate.py
user: login: sourcery-ai[bot]
body: **suggestion (review_instructions):** This function is missing a docstring to explain its purpose, parameters, and return value.

Adding a docstring would improve code readability and help other developers understand what this function does and how to use it correctly.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.py`

**Instructions:**
Add docstrings to functions and methods to explain their purpose and usage.

</details>
created_at: 2025-04-01T15:50:40Z
author_association: NONE
diff_hunk: @@ -44,38 +44,38 @@ def untangle_file_name(file_name):
     return regularized_untangled_file_name(groupdict)
 
 
-def to_float(key, decimal_separator='.'):
-    return float(key.replace(decimal_separator, '.'))
+def to_float(key, decimal_separator="."):
path: resources/experimental-set-calibration/Processing/calibrate.py
user: login: sourcery-ai[bot]
body: **suggestion (review_instructions):** This utility function is missing a docstring to explain its purpose and parameters.

Adding a docstring would make it clearer what this function does and how it should be used.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.py`

**Instructions:**
Add docstrings to functions and methods to explain their purpose and usage.

</details>
created_at: 2025-04-01T15:50:40Z
author_association: NONE

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-04-01T15:50:08Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide by Sourcery

This pull request replaces the existing pre-commit hooks with local format, lint, and typecheck scripts. It also migrates to `uv` for Python environment and dependency management. Additionally, it updates dependencies and configurations in `pyproject.toml` and includes minor changes to the release workflow and calibration script.

#### Sequence diagram for setting up Python environment with uv

```mermaid
sequenceDiagram
    participant GH Action
    participant uv

    GH Action->>uv: setup-uv@v5 with python-version
    activate uv
    uv->>uv: Install Python version
    uv->>uv: Enable cache
    deactivate uv

    GH Action->>uv: uv sync
    activate uv
    uv->>uv: Install project and dependencies
    deactivate uv
```

#### Updated class diagram for calibration script

```mermaid
classDiagram
    class CalibrationScript {
        -decimal_separator: str
        -data_path: pathlib.Path
        -reference_temperature: dict
        -temperature_data: dict
        -measured_temperature: dict
        -temperature_pairs: dict
        +regularized_untangled_file_name(details: dict) dict
        +untangle_file_name(file_name: str) dict
        +to_float(key: str, decimal_separator: str) float
        +represents_float(key: str, decimal_separator: str) bool
        +represents_float_list(keys: list, decimal_separator: str) bool
        +split_file(spamreader: csv.reader, decimal_separator: str) dict
        +fit_polynomial(x: np.array, y: np.array, degree: int) Pipeline
    }

    note for CalibrationScript "Updated decimal separator to comma."
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Replaced pre-commit hooks with local format, lint, and typecheck scripts. | <ul><li>Removed the usage of `unimport`, `black`, and `isort` pre-commit hooks.</li><li>Added local pre-commit hooks for formatting, linting, and type checking using shell scripts.</li><li>Introduced `scripts/format.sh`, `scripts/lint.sh`, and `scripts/typecheck.sh` to handle formatting, linting, and type checking respectively.</li><li>Configured pre-commit to use the new local hooks in `pre-commit-config.yaml`.</li></ul> | `.pre-commit-config.yaml`<br/>`scripts/format.sh`<br/>`scripts/lint.sh`<br/>`scripts/typecheck.sh` |
| Migrated to `uv` for Python environment and dependency management. | <ul><li>Replaced `setup-python` action with `astral-sh/setup-uv@v5` in `.github/actions/setup-python-environment/action.yaml`.</li><li>Updated CI workflow to use `uv sync` for installing dependencies in `.github/workflows/ci.yaml`.</li><li>Added `uv-lock` and `uv-sync` hooks to `.pre-commit-config.yaml`.</li><li>Modified test and vulture scripts to use `uv run`.</li></ul> | `.github/actions/setup-python-environment/action.yaml`<br/>`.github/workflows/ci.yaml`<br/>`.pre-commit-config.yaml`<br/>`scripts/test-cov.sh`<br/>`scripts/vulture.sh` |
| Updated dependencies and configurations in `pyproject.toml`. | <ul><li>Updated versions of development dependencies like `pytest`, `mypy`, and `ruff`.</li><li>Removed `funcy-stubs` from the `typecheck` dependencies.</li><li>Configured `ruff` linter with specific rules and ignores.</li></ul> | `pyproject.toml` |
| Minor changes to the release workflow and calibration script. | <ul><li>Minor formatting changes in `.github/workflows/release.yaml`.</li><li>Refactored the `calibrate.py` script to use double quotes for dictionary keys and f-strings for string formatting.</li></ul> | `.github/workflows/release.yaml`<br/>`resources/experimental-set-calibration/Processing/calibrate.py` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!
- **Generate a plan of action for an issue:** Comment `@sourcery-ai plan` on
  an issue to generate a plan of action for it.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: NONE
user: login: ruancomelli
created_at: 2025-04-01T15:50:16Z
body: * **#226** <a href="https://app.graphite.dev/github/pr/ruancomelli/boiling-learning/226?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/ruancomelli/boiling-learning/226?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: OWNER