Skip to content

Conversation

@GavinHuttley
Copy link
Collaborator

@GavinHuttley GavinHuttley commented Aug 19, 2025

Summary by Sourcery

Refine core library with type annotations and pathlib usage, strengthen checksum functions, enhance logging behavior, and bump Python support to 3.10+ while updating CI and tests accordingly.

Enhancements:

  • Add type annotations to functions and methods for better type safety
  • Switch path handling from os.path to pathlib.Path for abspath and directory creation
  • Refactor checksum utilities to use hashlib.md5 with usedforsecurity=False and stricter type checks
  • Improve CachingLogger by annotating attributes, streamlining log_file_path setter, and ensuring handler removal on shutdown
  • Update set_logger to log through a named logger instead of root handler

Build:

  • Raise minimum Python requirement to >=3.10 and target-version to py310 in pyproject.toml

CI:

  • Upgrade GitHub Actions workflows to Python 3.10–3.13 and adjust testing_develop matrix
  • Adjust nox sessions to run on Python 3.10–3.13

Tests:

  • Update test assertions to use literal dict syntax and numpy alias imports
  • Adjust expected behavior in logging tests for message content and record counts

@sourcery-ai
Copy link

sourcery-ai bot commented Aug 19, 2025

Reviewer's Guide

This PR modernizes the scitrack module by adopting pathlib and type hints, standardizing type checks and error messages, hardening MD5 usage, refactoring the CachingLogger internals, updating the logging setup, revising tests to match the new API, and bumping Python support to 3.10 in CI configurations.

Class diagram for updated CachingLogger and related functions

classDiagram
    class CachingLogger {
        - _started: bool
        - create_dir: bool
        - _messages: list[str]
        - _hostname: str
        - _mode: str
        - _log_file_path: str | None
        - _logfile: Any
        + __init__(log_file_path: os.PathLike | None = None, create_dir: bool = True, mode: str = "w")
        + _reset(mode: str = "w")
        + log_file_path: str | None
        + log_file_path(path: str)
        + mode: str
        + mode(mode: str)
        + _record_file(file_class: str, file_path: str)
        + input_file(file_path: str, label: str = "input_file_path")
        + output_file(file_path: str, label: str = "output_file_path")
        + text_data(data: str, label: str | None = None)
        + log_message(msg: str, label: str | None = None)
        + log_args(args: dict | None = None)
        + shutdown()
        + log_versions(packages: list[str] | str | None = None)
    }
    class Path {
    }
    class logging {
    }
    CachingLogger --> Path : uses
    CachingLogger --> logging : uses

    class set_logger {
        + set_logger(log_file_path: str | os.PathLike, level: int = logging.DEBUG, mode: str = "w") : logging.Handler
    }
    class get_file_hexdigest {
        + get_file_hexdigest(filename: str | os.PathLike) : str
    }
    class get_text_hexdigest {
        + get_text_hexdigest(data: str | bytes) : str
    }
Loading

File-Level Changes

Change Details Files
Adopt pathlib for path handling and add type annotations across functions
  • Rewrite abspath and _create_path to use Path.expanduser().resolve() and Path.mkdir()
  • Annotate function signatures (e.g., abspath, _create_path, get_version_for_package) with str
os.PathLike types
  • Remove legacy create_path alias and explicit FileHandler import
  • Standardize type checks and unify error messages
    • Use isinstance() for string, module, tuple/list checks in get_version_for_package and get_text_hexdigest
    • Assign exception messages to a single variable before raising
    • Add explicit frame fallback in log_versions to guard against None
    src/scitrack/__init__.py
    Harden MD5 checksum functions
    • Pass usedforsecurity=False to hashlib.md5() in get_file_hexdigest and get_text_hexdigest
    src/scitrack/__init__.py
    Refactor CachingLogger internals and log_file_path setter
    • Reorder and type-hint attributes in init, _reset, mode setter, _record_file, and log_message
    • Simplify directory creation and path resolution in log_file_path setter
    • Switch _messages to list[str] and enforce return None on void methods
    src/scitrack/__init__.py
    Update logging setup to target specific logger instances
    • Use logging.FileHandler directly in set_logger and retrieve the handler via its name
    • Log host/pid details and initial system info with logger.info instead of root logging
    src/scitrack/__init__.py
    Revise tests to align with updated API and style
    • Switch dict() assertions to literal syntax
    • Alias numpy imports to avoid name collisions
    • Remove redundant list() wrapping in set(records.values())
    • Adjust test file write_text syntax to match code formatter
    tests/test_logging.py
    Bump Python version support and update CI workflows
    • Raise requires-python to >=3.10 and drop Python 3.9 in pyproject.toml
    • Adjust GitHub Actions matrices to include 3.13 and remove older versions
    • Update nox session to run on Python 3.10 through 3.13
    pyproject.toml
    .github/workflows/release.yml
    .github/workflows/testing_develop.yml
    noxfile.py

    Tips and commands

    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!

    Customizing Your Experience

    Access your dashboard 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

    Copy link

    @sourcery-ai sourcery-ai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Hey there - I've reviewed your changes - here's some feedback:

    • In get_package_name, guard against inspect.getmodule returning None before accessing name to prevent unexpected attribute errors.
    • In log_args, replace the type(importlib) check with a clearer module‐type test (e.g. isinstance(x, types.ModuleType) or inspect.ismodule) for readability and correctness.
    • Ensure your GitHub Actions and nox matrices cover the same set of supported Python versions (e.g. re‐add 3.11/3.12 in testing_develop) so you’re consistently testing across all targets.
    Prompt for AI Agents
    Please address the comments from this code review:
    ## Overall Comments
    - In get_package_name, guard against inspect.getmodule returning None before accessing __name__ to prevent unexpected attribute errors.
    - In log_args, replace the `type(importlib)` check with a clearer module‐type test (e.g. isinstance(x, types.ModuleType) or inspect.ismodule) for readability and correctness.
    - Ensure your GitHub Actions and nox matrices cover the same set of supported Python versions (e.g. re‐add 3.11/3.12 in testing_develop) so you’re consistently testing across all targets.
    
    ## Individual Comments
    
    ### Comment 1
    <location> `src/scitrack/__init__.py:29` </location>
    <code_context>
    
    
    -def _create_path(path):
    +def _create_path(path: str | os.PathLike) -> None:
         """creates path"""
    -    if os.path.exists(path):
    </code_context>
    
    <issue_to_address>
    Potential confusion between file and directory creation in _create_path.
    
    If a file path is provided, this function will create a directory with that file's name. Please clarify the expected input or rename the function to prevent incorrect usage.
    
    Suggested implementation:
    
    ```python
    def _create_dir(dir_path: str | os.PathLike) -> None:
        """creates a directory at the specified path"""
        dir_path_obj: Path = Path(dir_path)
        if dir_path_obj.exists():
            return
    
        dir_path_obj.mkdir(parents=True, exist_ok=True)
    
    ```
    
    If `_create_path` is called elsewhere in the codebase, those references should be updated to `_create_dir` and ensure only directory paths are passed.
    </issue_to_address>
    
    ### Comment 2
    <location> `src/scitrack/__init__.py:68` </location>
    <code_context>
                 break
    -    if type(vn) in (tuple, list):
    +
    +    if isinstance(vn, (tuple, list)):
             vn = vn[0]
    
    </code_context>
    
    <issue_to_address>
    Unpacking only the first element of version tuple/list may lose information.
    
    Returning only the first element of a version tuple can misrepresent the actual version. Consider converting the entire tuple to a string for accurate reporting.
    </issue_to_address>
    
    <suggested_fix>
    <<<<<<< SEARCH
        if isinstance(vn, (tuple, list)):
            vn = vn[0]
    =======
        if isinstance(vn, (tuple, list)):
            vn = '.'.join(map(str, vn))
    >>>>>>> REPLACE
    
    </suggested_fix>
    
    ### Comment 3
    <location> `src/scitrack/__init__.py:117` </location>
    <code_context>
    +            raise AttributeError(msg)
    
    -        path = abspath(path)
    +        log_path: Path = Path(path).expanduser().resolve()
             if self.create_dir:
    -            dirname = os.path.dirname(path)
    </code_context>
    
    <issue_to_address>
    Using Path.resolve() may raise if the path does not exist.
    
    Use 'resolve(strict=False)' to prevent exceptions when the path does not exist, especially if you intend to create new files.
    </issue_to_address>
    
    ### Comment 4
    <location> `src/scitrack/__init__.py:221` </location>
    <code_context>
    -                packages[i] = p.__name__
    +                to_check[i] = p.__name__
    +
    +        frame: types.FrameType | None = inspect.currentframe()
    +        if frame is None:
    +            return
    </code_context>
    
    <issue_to_address>
    log_versions may leak frame references.
    
    Assigning 'frame' and 'parent' can create reference cycles and hinder garbage collection. Use 'del frame, parent' after they are no longer needed.
    </issue_to_address>

    Sourcery is free for open source - if you like our reviews please consider sharing them ✨
    Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

    @coveralls
    Copy link
    Collaborator

    coveralls commented Aug 19, 2025

    Pull Request Test Coverage Report for Build 17084324771

    Details

    • 0 of 0 changed or added relevant lines in 0 files are covered.
    • 6 unchanged lines in 2 files lost coverage.
    • Overall coverage decreased (-0.4%) to 98.286%

    Files with Coverage Reduction New Missed Lines %
    .nox/test-3-10/lib/python3.10/site-packages/scitrack/init.py 3 98.29%
    .nox/test-3-10/Lib/site-packages/scitrack/init.py 3 98.29%
    Totals Coverage Status
    Change from base Build 17083211324: -0.4%
    Covered Lines: 688
    Relevant Lines: 700

    💛 - Coveralls

    @GavinHuttley GavinHuttley merged commit 743f1f0 into HuttleyLab:develop Aug 19, 2025
    8 checks passed
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants