Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2a740a3
Pin Ruff (#72)
codedbyishika Jul 31, 2026
f35e78a
Separate linting before tests (#70)
codedbyishika Jul 31, 2026
d9f7765
Change Ruff linter output format to GitHub (#73)
codedbyishika Jul 31, 2026
78fce72
Merge branch 'main' of https://github.com/Imageomics/cautious-robot i…
codedbyishika Jul 31, 2026
c639912
Include MissingColumnsError
codedbyishika Jul 31, 2026
7a25b0e
Update __main__.py
codedbyishika Jul 31, 2026
a252ae1
Linting Fix
codedbyishika Jul 31, 2026
079e5c8
Update test file using exception
codedbyishika Jul 31, 2026
89cfc9a
Add ImageResizeError exception
codedbyishika Jul 31, 2026
e3ff1c9
Update Test Fixtures and remove raising exception for single image d…
codedbyishika Jul 31, 2026
9645795
Specify Exceptions
codedbyishika Jul 31, 2026
12ff135
Test Fixture
codedbyishika Jul 31, 2026
ca52143
Change to custom exceptions
codedbyishika Aug 2, 2026
a88b6f5
Update exceptions.py
codedbyishika Aug 2, 2026
a0f8b24
Update Exceptions
codedbyishika Aug 2, 2026
13677ba
Patch Fix and Mock Fix
codedbyishika Aug 2, 2026
5a971aa
Remove the Ignoring Ruff Error
codedbyishika Aug 4, 2026
86d7326
Remove Ruff Ignore for unused variable
codedbyishika Aug 4, 2026
20ad23b
Closing Files in Teardown
codedbyishika Aug 4, 2026
3143cde
Update File Handling
codedbyishika Aug 10, 2026
81fa1bb
Update exposing instance methods to include buddy_check
codedbyishika Aug 13, 2026
ea5f5e8
Apply suggestions from code review
codedbyishika Aug 14, 2026
1b30b8d
Ruff Fix
codedbyishika Aug 14, 2026
069fa47
Update to include more exceptions
codedbyishika Aug 14, 2026
edeee5e
Update __main__.py
codedbyishika Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions .github/workflows/run-push-tests.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
name: run-PR-tests
name: run-tests

on:
pull_request:
push:
branches: main

jobs:
lint:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: install dependencies
run: pip install .[dev]
shell: bash

- name: run Ruff linter
run: ruff check --output-format=github .
shell: bash

run-tests:
needs: lint
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -24,10 +44,6 @@ jobs:
- name: install dependencies
run: pip install .[dev]
shell: bash

- name: run Ruff linter
run: ruff check .
shell: bash

- name: run tests
run: pytest
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ ipython_config.py
.pdm-python
.pdm-build/

# uv
uv.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

Expand Down
9 changes: 6 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.2
- repo: local
hooks:
- id: ruff

name: ruff
entry: uv run --extra dev ruff check --force-exclude
language: system
types_or: [python, pyi]
require_serial: true
14 changes: 7 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ dependencies = [
"sum-buddy",
]

[project.optional-dependencies]
dev = [
"pytest",
"ruff",
"pre-commit"
]

keywords = [
"imageomics",
"metadata",
Expand All @@ -52,6 +45,13 @@ keywords = [
"sum-buddy",
]

[project.optional-dependencies]
dev = [
"pytest",
"ruff==0.16.0",
"pre-commit"
]

[project.urls]
Homepage = "https://github.com/Imageomics/cautious-robot"
Repository = "https://github.com/Imageomics/cautious-robot.git"
Expand Down
9 changes: 1 addition & 8 deletions src/cautiousrobot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,4 @@
from cautiousrobot.download import download_images
from cautiousrobot.utils import downsample_and_save_image

# Create instance of the class
buddy_check_instance = BuddyCheck()

# Expose instance methods by executing them
buddy_check_instance.validate_download #noqa: B018
buddy_check_instance.check_alignment #noqa: B018

__all__ = ["check_alignment", "download_images", "downsample_and_save_image", "validate_download"]
__all__ = ["BuddyCheck", "download_images", "downsample_and_save_image"]
19 changes: 7 additions & 12 deletions src/cautiousrobot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

import argparse
import hashlib
import sys

import pandas as pd
from sumbuddy import get_checksums

from cautiousrobot.__about__ import __version__
from cautiousrobot.buddy_check import BuddyCheck
from cautiousrobot.download import download_images
from cautiousrobot.exceptions import BuddyCheckError, ChecksumError
from cautiousrobot.roll_call import RollCall
from cautiousrobot.utils import process_csv

Expand Down Expand Up @@ -77,9 +77,8 @@ def process_checksums(img_dir, metadata_path, args, source_df):
print(f"There are {checksum_df.shape[0]} files in {img_dir}. Based on {args.input_file}, there should be {expected_num_imgs} images.")

return checksum_df, expected_num_imgs
except Exception as e: # noqa: BLE001
print(f"checksum calculation of downloaded images was unsuccessful due to {e}.")
print(f"you can get checksums for the images downloaded to {img_dir} by running sum-buddy directly.")
except (ChecksumError, OSError, ValueError, pd.errors.ParserError) as e:
print(e)
return None, None


Expand All @@ -102,10 +101,8 @@ def verify_downloads(args, source_df, checksum_df, filename_col, metadata_path,
print(f"See {metadata_path}_missing.csv for missing image info and check logs.")
else:
print(f"Buddy check successful. All {expected_num_imgs} expected images accounted for.")
except Exception as e: # noqa: BLE001
print(f"Verification of download failed due to {type(e).__name__}: {e}.")
print("'BuddyCheck.validate_download' can be run directly on DataFrames of the source and checksum CSVs after correcting for this error.")

except (BuddyCheckError, OSError, ValueError, KeyError) as e:
print(e)

def main():
args = parse_args()
Expand All @@ -118,10 +115,8 @@ def main():

# Set up expected columns and process CSV
expected_cols, subfolders = roll_call.setup_expected_columns(args)
try:
data_df = process_csv(csv_path, expected_cols)
except Exception as missing_cols: # noqa: BLE001
sys.exit(f"{missing_cols} Please adjust inputs and try again.")
data_df = process_csv(csv_path, expected_cols) #Exception handling process_csv


# Validate data and handle missing filenames
filename_col = expected_cols["filename_col"]
Expand Down
6 changes: 3 additions & 3 deletions src/cautiousrobot/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_content_type_from_url(url):
content_type = response.headers.get('content-type', '')
# Split off parameters like '; charset=utf-8'
return content_type.split(';')[0].strip() if content_type else None
except Exception: # noqa: BLE001
except requests.RequestException:
return None


Expand Down Expand Up @@ -273,15 +273,15 @@ def download_single_image(url, image_name, image_dir_path, log_data, log_errors,
while redo and attempts_remaining > 0:
try:
response = requests.get(url, stream=True)
except Exception as e: # noqa: BLE001
except requests.RequestException as error:
redo = True
attempts_remaining -= 1
if attempts_remaining <= 0:
log_errors = log_response(log_errors,
index=i,
image=image_name,
file_path=url,
response_code=str(e))
response_code=str(error))
update_log(log=log_errors, index=i, filepath=error_log_filepath)
continue

Expand Down
20 changes: 20 additions & 0 deletions src/cautiousrobot/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,23 @@ class EmptyDataFrameError(Exception):
def __init__(self, df_name):
message = f"Input DataFrame {df_name} is empty."
super().__init__(message)

class MissingColumnsError(Exception):
def __init__(self, csv_path, column_names, expected_columns):
message = f"The CSV at {csv_path} is missing column(s): {column_names}, defined as {expected_columns}."
super().__init__(message)

class ChecksumError(Exception):
def __init__(self, error, img_dir):
self.error = error
self.img_dir = img_dir
message = f"Checksum calculation of downloaded images was unsuccessful due to {self.error}.\n You can get checksums for the images downloaded to {self.img_dir} by running sum-buddy directly."
super().__init__(message)

class BuddyCheckError(Exception):
def __init__(self, error, reason):
self.error = error
self.reason = reason
message = f"Verification of download failed due to {self.error}: {self.reason}.\n'BuddyCheck.validate_download' can be run directly on DataFrames of the source and checksum CSVs after correcting for this error."
super().__init__(message)

11 changes: 6 additions & 5 deletions src/cautiousrobot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import os

import pandas as pd
from PIL import Image
from PIL import Image, UnidentifiedImageError

from cautiousrobot.exceptions import MissingColumnsError


def log_response(log_data, index, image, file_path, response_code):
Expand Down Expand Up @@ -44,7 +46,7 @@ def process_csv(csv_path, expected_cols):
if expected_cols[col] not in list(df.columns):
missing_cols.append(col)
if len(missing_cols) > 0:
raise Exception(f"The CSV at {csv_path} is missing column(s): {missing_cols}, defined as {[expected_cols[col] for col in missing_cols]}.") # noqa: TRY002
raise MissingColumnsError(csv_path, missing_cols, [expected_cols[col] for col in missing_cols])

return df

Expand All @@ -71,13 +73,12 @@ def downsample_and_save_image(image_dir_path, image_name, downsample_dir_path, d
try:
img = Image.open(f"{image_dir_path}/{image_name}")
img.resize((downsample_size, downsample_size)).save(f"{downsample_dir_path}/{image_name}")
except Exception as e: # noqa: BLE001
print(e)
except (OSError, UnidentifiedImageError) as error:
log_errors = log_response(
log_errors,
index=image_index,
image="downsized_" + image_name,
file_path=file_path,
response_code=str(e)
response_code=str(error)
)
update_log(log=log_errors, index=image_index, filepath=error_log_filepath)
21 changes: 11 additions & 10 deletions tests/test_buddycheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ def setUp(self):
self.buddy_check_filename = BuddyCheck(buddy_id='filename')
self.buddy_check_id_col = BuddyCheck(buddy_id = "filename", buddy_col = "sha256")

self.img_source_file = tempfile.NamedTemporaryFile(delete=False, mode='w') # noqa: SIM115
self.checksum_source_file = tempfile.NamedTemporaryFile(delete=False, mode='w') # noqa: SIM115
#self.img_source_file = tempfile.NamedTemporaryFile(delete=False, mode='w')
#self.checksum_source_file = tempfile.NamedTemporaryFile(delete=False, mode='w')

self.img_source_file.write("""filename,checksum

with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix=".csv") as f:
f.write("""filename,checksum
image1.jpg,abc123
image2.jpg,def456
image3.jpg,ghi789
""")
self.img_source_file.close()
image3.jpg,ghi789""")
self.img_source_file = f

self.checksum_source_file.write("""filename,md5
with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix=".csv") as f:
f.write("""filename,md5
image1.jpg,abc123
image2.jpg,def456
image3.jpg,ghi789
""")
self.checksum_source_file.close()
image3.jpg,ghi789""")
self.checksum_source_file = f


def tearDown(self):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from unittest.mock import patch

import pandas as pd
import requests

from cautiousrobot.download import download_images

Expand All @@ -16,7 +17,7 @@
class TestDownload(unittest.TestCase):
@patch('requests.get')
def test_response_exception(self, get_mock):
get_mock.side_effect = Exception
get_mock.side_effect = requests.exceptions.ConnectionError("Connection failed")
download_images(DUMMY_DATA, img_dir = IMG_DIR, log_filepath = LOG_FILEPATH,
error_log_filepath = ERROR_LOG_FILEPATH)

Expand Down
Loading
Loading