From 21777a4b668fc2e396b04e2c343a120046b7b5ba Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 9 Jul 2026 00:52:31 -0500 Subject: [PATCH 1/2] refactor(logging): route library print() through bt.logging Convert 26 print() calls in library internals to bt.logging at appropriate levels (error for caught exceptions, warning for recoverable/missing-file cases, debug for success detail): - gas/cache/media_storage.py (12), gas/cache/util/video.py (10), gas/cache/util/filesystem.py (2), gas/generation/util/image.py (2) - add 'import bittensor as bt' where missing; drop now-unused 'import sys' Left as-is (genuine user-facing CLI stdout, not logging): miner_stats.py, push_model.py, verify_c2pa.py, cli.py, miner_requests.py upload progress, and print_directory_tree's display fallback (which already supports an injected log_func). --- gas/cache/media_storage.py | 25 +++++++++++++------------ gas/cache/util/filesystem.py | 5 ++--- gas/cache/util/video.py | 21 +++++++++++---------- gas/generation/util/image.py | 5 +++-- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/gas/cache/media_storage.py b/gas/cache/media_storage.py index 46dee9a5..bd8b095e 100644 --- a/gas/cache/media_storage.py +++ b/gas/cache/media_storage.py @@ -3,6 +3,7 @@ from typing import Dict, Any, Optional, List from pathlib import Path +import bittensor as bt import cv2 import numpy as np from diffusers.utils import export_to_video @@ -107,7 +108,7 @@ def write_media(self, media_data: "Media") -> tuple[Optional[str], Optional[str] return str(save_path), str(mask_path) if mask_path else None except Exception as e: - print(f"Error writing media: {e}") + bt.logging.error(f"Error writing media: {e}") return None, None def _write_image_content(self, media_data: "Media", save_path: Path) -> None: @@ -162,12 +163,12 @@ def read_image( """ try: if not image_path.exists(): - print(f"Image file not found: {image_path}") + bt.logging.warning(f"Image file not found: {image_path}") return None image = cv2.imread(str(image_path)) if image is None: - print(f"Failed to load image: {image_path}") + bt.logging.warning(f"Failed to load image: {image_path}") return None if as_rgb: @@ -182,7 +183,7 @@ def read_image( return image except Exception as e: - print(f"Error reading image {image_path}: {e}") + bt.logging.error(f"Error reading image {image_path}: {e}") return None def read_mask(self, mask_path: Path) -> Optional[np.ndarray]: @@ -195,13 +196,13 @@ def read_mask(self, mask_path: Path) -> Optional[np.ndarray]: """ try: if not mask_path.exists(): - print(f"Mask file not found: {mask_path}") + bt.logging.warning(f"Mask file not found: {mask_path}") return None return np.load(mask_path) except Exception as e: - print(f"Error reading mask {mask_path}: {e}") + bt.logging.error(f"Error reading mask {mask_path}: {e}") return None def read_video_frames( @@ -223,7 +224,7 @@ def read_video_frames( """ try: if not video_path.exists(): - print(f"Video file not found: {video_path}") + bt.logging.warning(f"Video file not found: {video_path}") return None return sample_frames( @@ -234,7 +235,7 @@ def read_video_frames( ) except Exception as e: - print(f"Error reading video {video_path}: {e}") + bt.logging.error(f"Error reading video {video_path}: {e}") return None def list_media_files( @@ -286,7 +287,7 @@ def delete_media_file(self, file_path: Path) -> bool: return True except Exception as e: - print(f"Error deleting media file {file_path}: {e}") + bt.logging.error(f"Error deleting media file {file_path}: {e}") return False def retrieve_media( @@ -314,7 +315,7 @@ def retrieve_media( for media_entry in media_entries: file_path = Path(media_entry.file_path) if not file_path.exists(): - print(f"Media file not found: {file_path}") + bt.logging.warning(f"Media file not found: {file_path}") continue try: @@ -335,7 +336,7 @@ def retrieve_media( mask_path = file_path.parent / (file_path.stem + "_mask.npy") mask = self.read_mask(mask_path) if mask is None: - print(f"Mask not found for {file_path}") + bt.logging.warning(f"Mask not found for {file_path}") continue item = { @@ -372,6 +373,6 @@ def retrieve_media( sampled_items.append(item) except Exception as e: - print(f"Error processing {file_path}: {e}") + bt.logging.error(f"Error processing {file_path}: {e}") return {"count": len(sampled_items), "items": sampled_items} diff --git a/gas/cache/util/filesystem.py b/gas/cache/util/filesystem.py index 25e6a557..f00d4311 100644 --- a/gas/cache/util/filesystem.py +++ b/gas/cache/util/filesystem.py @@ -1,5 +1,4 @@ import asyncio -import sys import time from pathlib import Path from typing import Any, Callable, Dict, List, Optional, Tuple, Union @@ -81,7 +80,7 @@ def get_dir_size( total_size += subdir_size file_count += subdir_count except (PermissionError, OSError) as e: - print(f"Error accessing {path}: {e}", file=sys.stderr) + bt.logging.warning(f"Error accessing {path}: {e}") return total_size, file_count @@ -181,7 +180,7 @@ def analyze_directory( if log_func: log_func(error_msg) else: - print(error_msg, file=sys.stderr) + bt.logging.warning(error_msg) return result diff --git a/gas/cache/util/video.py b/gas/cache/util/video.py index 2d5bd238..f54c1db9 100644 --- a/gas/cache/util/video.py +++ b/gas/cache/util/video.py @@ -6,6 +6,7 @@ from pathlib import Path from typing import Any, Dict, Optional, Tuple +import bittensor as bt import ffmpeg import numpy as np from PIL import Image @@ -53,7 +54,7 @@ def sample_frames( height = int(video_info.get("height", 256)) reported_fps = float(video_info.get("fps", max_fps)) except Exception as e: - print(f"Unable to extract video metadata from {str(video_path)}: {e}") + bt.logging.warning(f"Unable to extract video metadata from {str(video_path)}: {e}") return None if ( @@ -61,7 +62,7 @@ def sample_frames( or reported_fps <= 0 or not math.isfinite(reported_fps) ): - print(f"Unreasonable FPS ({reported_fps}) detected in {video_path}, capping at {max_fps}") + bt.logging.warning(f"Unreasonable FPS ({reported_fps}) detected in {video_path}, capping at {max_fps}") frame_rate = max_fps else: frame_rate = reported_fps @@ -106,19 +107,19 @@ def sample_frames( frame.load() # Verify image can be loaded frames.append(np.array(frame)) except Exception as e: - print(f"Failed to process frame at {timestamp}s: {e}") + bt.logging.warning(f"Failed to process frame at {timestamp}s: {e}") continue except ffmpeg.Error as e: - print(f"FFmpeg error at {timestamp}s: {e.stderr.decode()}") + bt.logging.warning(f"FFmpeg error at {timestamp}s: {e.stderr.decode()}") continue if len(no_data) > 0: tmin, tmax = min(no_data), max(no_data) - print(f"No data received for {len(no_data)} frames between {tmin} and {tmax}") + bt.logging.warning(f"No data received for {len(no_data)} frames between {tmin} and {tmax}") if not frames: - print(f"No frames successfully extracted from {video_path}") + bt.logging.warning(f"No frames successfully extracted from {video_path}") return None frames = np.stack(frames, axis=0) @@ -139,7 +140,7 @@ def sample_frames( with open(metadata_path, "r") as f: metadata = json.load(f) except Exception as e: - print(f"Error loading metadata for {video_path}: {e}") + bt.logging.warning(f"Error loading metadata for {video_path}: {e}") result = { "video": frames, @@ -156,14 +157,14 @@ def sample_frames( }, } - print(f"Successfully sampled {actual_duration}s segment ({len(frames)} frames)") + bt.logging.debug(f"Successfully sampled {actual_duration}s segment ({len(frames)} frames)") return result except Exception as e: - print(f"Error sampling from {video_path}: {e}") + bt.logging.warning(f"Error sampling from {video_path}: {e}") continue - print("Failed to sample any video after multiple attempts") + bt.logging.error("Failed to sample any video after multiple attempts") return None diff --git a/gas/generation/util/image.py b/gas/generation/util/image.py index 624baf67..6a204ca9 100644 --- a/gas/generation/util/image.py +++ b/gas/generation/util/image.py @@ -1,6 +1,7 @@ import numpy as np import PIL import os +import bittensor as bt from PIL import Image, ImageDraw from typing import Tuple, Union, List @@ -65,9 +66,9 @@ def save_images_to_disk( # if resize: # image = resize_image(image, TARGET_IMAGE_SIZE[0], TARGET_IMAGE_SIZE[1]) image.save(file_path, "JPEG") # Save the image - print(f"Saved: {file_path}") + bt.logging.debug(f"Saved: {file_path}") except Exception as e: - print(f"Failed to save image {i}: {e}") + bt.logging.error(f"Failed to save image {i}: {e}") def ensure_mask_3d(mask: np.ndarray) -> np.ndarray: From 5d03bcc4ac42a7d32a9c1e0d715d36352cf60d12 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 9 Jul 2026 01:08:29 -0500 Subject: [PATCH 2/2] ci: enforce no print() in library code via ruff T201 Adds T201 to the CI ruff select so print() in library code fails the build. CLI presenters and tools keep stdout output via per-file-ignores in pyproject (miner_stats, miner_requests, push_model, verify_c2pa, scripts/, tests/); print_directory_tree's display fallback gets a line-level noqa. This makes the print->bt.logging conversion durable instead of a one-time cleanup. --- .github/workflows/ci.yml | 7 ++++--- gas/cache/util/filesystem.py | 2 +- pyproject.toml | 10 ++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b282b3f..136c84bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,10 @@ jobs: steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 - # Error-level rules only for now (syntax errors, undefined names, - # invalid prints/f-strings). Widen as the Phase 2 burndown lands. - - run: uvx ruff check --select E9,F63,F7,F82 gas neurons tests + # Error-level rules plus T201 (no print() in library code — CLI + # presenters are exempted via per-file-ignores in pyproject). + # Widen further as the Phase 2 burndown lands. + - run: uvx ruff check --select E9,F63,F7,F82,T201 gas neurons tests test: runs-on: ubuntu-latest diff --git a/gas/cache/util/filesystem.py b/gas/cache/util/filesystem.py index f00d4311..7720841e 100644 --- a/gas/cache/util/filesystem.py +++ b/gas/cache/util/filesystem.py @@ -214,7 +214,7 @@ def print_directory_tree( if log_func: log_func(tree_line) else: - print(tree_line) + print(tree_line) # noqa: T201 - display fallback for interactive tree output num_subdirs = len(tree_data["subdirs"]) diff --git a/pyproject.toml b/pyproject.toml index 3e2383df..5365123a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,16 @@ dev = ["pytest>=8.0"] testpaths = ["tests"] pythonpath = ["."] +[tool.ruff.lint.per-file-ignores] +# T201 (print): allowed in genuine CLI presenters / tools / scripts, where +# stdout is the intended output. Library code must use bt.logging. +"gas/cache/util/miner_stats.py" = ["T201"] +"gas/protocol/miner_requests.py" = ["T201"] +"neurons/discriminator/push_model.py" = ["T201"] +"neurons/generator/helper/verify_c2pa.py" = ["T201"] +"scripts/**" = ["T201"] +"tests/**" = ["T201"] + [tool.setuptools] packages = { find = { where = [ ".",