refactor: replace print() with logging (T201)

- Converted 67 print statements to logging across 11 files
- Added logging.basicConfig(level=logging.INFO) to each file
- Used appropriate log levels: info, warning, error, exception
- Removed T201 from ruff ignore list to enforce logging usage
This commit is contained in:
Krzysztof kuhy Rudnicki 2025-11-30 14:36:13 +01:00
parent 742356d295
commit 8a23327e92
12 changed files with 117 additions and 75 deletions

View File

@ -1,9 +1,12 @@
import json
import logging
import os
from pathlib import Path
import requests
logging.basicConfig(level=logging.INFO)
requests_send = 0
while requests_send < 90:
res = requests.get("https://api.thecatapi.com/v1/images/search?limit=100&api_key=")
@ -28,7 +31,7 @@ while requests_send < 90:
with open(image_path, "wb") as file:
file.write(response.content)
print(f"Saved {url} as {image_path}")
logging.info(f"Saved {url} as {image_path}")
except requests.exceptions.RequestException as e:
print(f"Failed to download {url}: {e}")
logging.exception(f"Failed to download {url}: {e}")

View File

@ -12,9 +12,12 @@ from __future__ import annotations
import argparse
from html.parser import HTMLParser
import logging
import os
from urllib.parse import urlparse
logging.basicConfig(level=logging.INFO)
class _HrefParser(HTMLParser):
def __init__(self) -> None:
@ -84,7 +87,7 @@ def main() -> int:
with open(out_path, "w", encoding="utf-8") as f:
f.writelines(f"*{host}*\n" for host in hosts)
print(f"Wrote {len(hosts)} host(s) to {out_path}")
logging.info(f"Wrote {len(hosts)} host(s) to {out_path}")
return 0

View File

@ -1,10 +1,13 @@
import json
import logging
import os
import random
import sys
import pygame
logging.basicConfig(level=logging.INFO)
# Initialize Pygame
pygame.init()
@ -95,7 +98,9 @@ class KeyboardCoopGame:
# Convert to set for faster lookup (we only need the keys)
return set(dictionary_data.keys())
except FileNotFoundError:
print("Warning: words_dictionary.json not found, using fallback dictionary")
logging.warning(
"words_dictionary.json not found, using fallback dictionary"
)
# Fallback to a smaller dictionary if file not found
return {
"cat",
@ -149,9 +154,8 @@ class KeyboardCoopGame:
"good",
}
except json.JSONDecodeError:
print(
"Warning: Error reading words_dictionary.json, "
"using fallback dictionary"
logging.warning(
"Error reading words_dictionary.json, " "using fallback dictionary"
)
return {
"cat",

View File

@ -35,6 +35,7 @@ from __future__ import annotations
from dataclasses import dataclass
import io
import logging
import os
import re
import sys
@ -42,6 +43,8 @@ import sys
import chess
import chess.pgn
logging.basicConfig(level=logging.INFO)
@dataclass
class Blunder:
@ -316,30 +319,34 @@ def _process_single_log(log_path: str) -> int:
with open(log_path, encoding="utf-8") as fh:
text = fh.read()
except FileNotFoundError:
print(f"Log file not found: {log_path}")
logging.exception(f"Log file not found: {log_path}")
return 2
try:
blunders = parse_columns_for_blunders(text)
except Exception as e:
print(f"Error parsing Columns in {os.path.basename(log_path)}: {e}")
logging.exception(f"Error parsing Columns in {os.path.basename(log_path)}: {e}")
return 2
if not blunders:
print(f"No blunders found in Columns section: {os.path.basename(log_path)}")
logging.warning(
f"No blunders found in Columns section: {os.path.basename(log_path)}"
)
return 1
pgn_text = extract_pgn(text)
if not pgn_text:
print(f"No PGN section found: {os.path.basename(log_path)}")
logging.warning(f"No PGN section found: {os.path.basename(log_path)}")
return 1
try:
cases = fen_and_uci_for_blunders(pgn_text, blunders)
except Exception as e:
print(f"Error converting SAN to UCI in {os.path.basename(log_path)}: {e}")
logging.exception(
f"Error converting SAN to UCI in {os.path.basename(log_path)}: {e}"
)
return 2
if not cases:
print(
logging.warning(
f"Failed to reconstruct any blunder positions "
f"from PGN: {os.path.basename(log_path)}"
)
@ -355,7 +362,7 @@ def _process_single_log(log_path: str) -> int:
)
unified = os.path.abspath(unified)
added = append_cases_to_unified_test(unified, cases)
print(
logging.info(
f"Appended {added} new blunder checks to "
f"{os.path.relpath(unified)} (game {game_id})."
)
@ -369,7 +376,7 @@ def main(argv: list[str]) -> int:
# No argument: process all logs in past_games
if len(argv) == 1:
if not os.path.isdir(past_dir):
print(f"No past_games directory found at {past_dir}")
logging.error(f"No past_games directory found at {past_dir}")
return 2
logs = [
os.path.join(past_dir, name)
@ -377,7 +384,7 @@ def main(argv: list[str]) -> int:
if re.match(r"lichess_bot_game_[A-Za-z0-9]+\.log$", name)
]
if not logs:
print(f"No logs found in {past_dir}")
logging.warning(f"No logs found in {past_dir}")
return 1
# Sort by mtime ascending for determinism
logs.sort(key=lambda p: os.path.getmtime(p))
@ -386,7 +393,7 @@ def main(argv: list[str]) -> int:
rc = _process_single_log(lp)
if rc == 0:
ok += 1
print(
logging.info(
f"Processed {len(logs)} logs from {past_dir}, "
f"succeeded: {ok}, failed: {len(logs) - ok}"
)
@ -407,7 +414,7 @@ def main(argv: list[str]) -> int:
candidate_path = maybe
if not candidate_path:
print("Usage: generate_blunder_tests.py [<game_id>|</path/to/log>]")
logging.info("Usage: generate_blunder_tests.py [<game_id>|</path/to/log>]")
return 2
return _process_single_log(candidate_path)

View File

@ -1,10 +1,13 @@
import argparse
from datetime import datetime
import logging
import os
import random
from PIL import Image
logging.basicConfig(level=logging.INFO)
def generate_bloated_jpeg(
size, color_list, block_size, output_path, quality, image_index, folder
@ -120,13 +123,15 @@ if __name__ == "__main__":
folder = f"generated_images_{timestamp}"
# Display used parameters
print(f"Generating {args.num_images} image(s) with the following parameters:")
print(f" Size: {args.size}")
print(f" Colors: {args.colors}")
print(f" Block size: {args.block_size}")
print(f" Base output path: {args.output_path}")
print(f" Quality: {args.quality}")
print(f" Output folder: {folder}")
logging.info(
f"Generating {args.num_images} image(s) with the following parameters:"
)
logging.info(f" Size: {args.size}")
logging.info(f" Colors: {args.colors}")
logging.info(f" Block size: {args.block_size}")
logging.info(f" Base output path: {args.output_path}")
logging.info(f" Quality: {args.quality}")
logging.info(f" Output folder: {folder}")
# Generate the specified number of images
for i in range(1, args.num_images + 1):
@ -139,4 +144,4 @@ if __name__ == "__main__":
i,
folder,
)
print(f"Image {i} saved to {os.path.abspath(output_path)}")
logging.info(f"Image {i} saved to {os.path.abspath(output_path)}")

View File

@ -1,8 +1,11 @@
import contextlib
import logging
import random
import re
import sys
logging.basicConfig(level=logging.INFO)
def randomize_numbers(numbers, min_percentage=1, max_percentage=20):
randomized_numbers = []
@ -38,7 +41,7 @@ def parse_input(input_string):
if __name__ == "__main__":
if len(sys.argv) < 2:
print(
logging.info(
"Usage: python random_digits.py <number1> <number2> ... "
"[min_percentage max_percentage]"
)
@ -67,9 +70,9 @@ if __name__ == "__main__":
format_str = f".{decimal_counts[i]}f"
formatted_numbers.append(float(format(num, format_str)))
print("Original numbers:", numbers)
print("Randomized numbers:", formatted_numbers)
logging.info(f"Original numbers: {numbers}")
logging.info(f"Randomized numbers: {formatted_numbers}")
except ValueError as e:
print(f"Error: {e}")
print("Please provide valid numbers and percentages.")
logging.exception(f"Error: {e}")
logging.exception("Please provide valid numbers and percentages.")
sys.exit(1)

View File

@ -1,4 +1,5 @@
import argparse
import logging
import os
from urllib.parse import urlparse
@ -6,6 +7,8 @@ import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
logging.basicConfig(level=logging.INFO)
# Initialize argument parser to accept the website URL as an argument
parser = argparse.ArgumentParser(description="Download images from a comic website.")
parser.add_argument(
@ -18,7 +21,7 @@ driver = webdriver.Chrome()
# Open the website from the passed argument
url = args.url
print(f"Opening the website: {url}")
logging.info(f"Opening the website: {url}")
driver.get(url)
@ -29,13 +32,13 @@ def download_image(url):
# Check if the image already exists
if os.path.exists(image_name):
print(f"Image {image_name} already exists, skipping download.")
logging.info(f"Image {image_name} already exists, skipping download.")
return False
print(f"Downloading image from URL: {url}")
logging.info(f"Downloading image from URL: {url}")
img_data = requests.get(url).content
with open(image_name, "wb") as handler:
handler.write(img_data)
print(f"Image {image_name} downloaded successfully")
logging.info(f"Image {image_name} downloaded successfully")
return True
@ -43,14 +46,14 @@ def download_image(url):
count = 1
while True:
print(f"Processing image {count}...")
logging.info(f"Processing image {count}...")
# Find the image element by its ID
image_element = driver.find_element(By.ID, "cc-comic")
# Get the image URL from the 'src' attribute
image_url = image_element.get_attribute("src")
print(f"Found image URL: {image_url}")
logging.info(f"Found image URL: {image_url}")
# Download the image if it doesn't already exist
if download_image(image_url):
@ -58,7 +61,7 @@ while True:
# Try to find the 'Next' button by its class
try:
print("Clicking the 'Next' button to load the next image...")
logging.info("Clicking the 'Next' button to load the next image...")
next_button = driver.find_element(By.CSS_SELECTOR, "a.cc-next")
# Navigate to the URL in the 'href' of the next button
@ -67,9 +70,9 @@ while True:
except:
# If the 'Next' button is not found, it means we've reached the last image
print("No 'Next' button found. Reached the end of images.")
logging.info("No 'Next' button found. Reached the end of images.")
break
# Close the browser
print("All images processed, closing the browser.")
logging.info("All images processed, closing the browser.")
driver.quit()

View File

@ -5,10 +5,13 @@ Requires user to log their workout to unlock the screen.
from datetime import datetime
import json
import logging
import os
import sys
import tkinter as tk
logging.basicConfig(level=logging.INFO)
class ScreenLocker:
def __init__(self, demo_mode=True):
@ -18,7 +21,7 @@ class ScreenLocker:
# Check if already logged today
if self.has_logged_today():
print("Workout already logged today. Skipping screen lock.")
logging.info("Workout already logged today. Skipping screen lock.")
sys.exit(0)
self.root = tk.Tk()
@ -632,7 +635,7 @@ class ScreenLocker:
with open(self.log_file, "w") as f:
json.dump(logs, f, indent=2)
except OSError as e:
print(f"Warning: Could not save workout log: {e}")
logging.warning(f"Could not save workout log: {e}")
def close(self):
self.root.destroy()

View File

@ -24,6 +24,7 @@ from __future__ import annotations
import argparse
import contextlib
import io
import logging
import multiprocessing
import os
import re
@ -39,10 +40,8 @@ try:
import chess.engine
import chess.pgn
except Exception: # pragma: no cover
print("Missing dependency. Please install python-chess:", file=sys.stderr)
print(
" pip install -r PYTHON/stockfish_analysis/requirements.txt", file=sys.stderr
)
logging.exception("Missing dependency. Please install python-chess:")
logging.exception(" pip install -r PYTHON/stockfish_analysis/requirements.txt")
raise
@ -202,6 +201,7 @@ def _auto_hash_mb(threads_wanted: int, engine_options) -> int:
def main():
logging.basicConfig(level=logging.INFO, format="%(message)s")
ap = argparse.ArgumentParser(
description="Analyze a chess game's moves with Stockfish and rate each move."
)
@ -256,7 +256,7 @@ def main():
args = ap.parse_args()
if not os.path.isfile(args.file):
print(f"Input not found: {args.file}", file=sys.stderr)
logging.error(f"Input not found: {args.file}")
sys.exit(1)
with open(args.file, encoding="utf-8", errors="replace") as f:
@ -264,22 +264,21 @@ def main():
pgn_text = extract_pgn_text(raw)
if not pgn_text:
print("Could not locate PGN text in the file.", file=sys.stderr)
logging.error("Could not locate PGN text in the file.")
sys.exit(2)
game = chess.pgn.read_game(io.StringIO(pgn_text))
if game is None:
print("Failed to parse PGN.", file=sys.stderr)
logging.error("Failed to parse PGN.")
sys.exit(3)
# Prepare engine
try:
engine = chess.engine.SimpleEngine.popen_uci([args.engine])
except FileNotFoundError:
print(f"Could not launch engine at: {args.engine}", file=sys.stderr)
print(
"Ensure Stockfish is installed and in PATH, or specify with --engine.",
file=sys.stderr,
logging.exception(f"Could not launch engine at: {args.engine}")
logging.exception(
"Ensure Stockfish is installed and in PATH, or specify with --engine."
)
sys.exit(4)
@ -348,13 +347,13 @@ def main():
limit = chess.engine.Limit(time=max(0.05, args.time))
board = game.board()
print("Game:")
logging.info("Game:")
white = game.headers.get("White", "White")
black = game.headers.get("Black", "Black")
result = game.headers.get("Result", "*")
print(f" {white} vs {black} Result: {result}")
print()
print(
logging.info(f" {white} vs {black} Result: {result}")
logging.info("")
logging.info(
"Columns: ply side move played_eval best_eval loss class best_suggestion"
)
# Brief performance summary (best-effort)
@ -371,12 +370,14 @@ def main():
except Exception:
hash_show = None
if hash_show is not None:
print(
logging.info(
f"Using engine options: Threads={thr_show}, "
f"Hash={hash_show} MB, MultiPV={effective_mpv}"
)
else:
print(f"Using engine options: Threads={thr_show}, " f"MultiPV={effective_mpv}")
logging.info(
f"Using engine options: Threads={thr_show}, MultiPV={effective_mpv}"
)
ply = 1
try:
@ -385,7 +386,7 @@ def main():
if args.last_move_only:
# Walk to the last move in the main line and analyze only that ply.
if not node.variations:
print("No moves found in the game.")
logging.warning("No moves found in the game.")
else:
while node.variations:
move_node = node.variations[0]
@ -486,7 +487,7 @@ def main():
classification = classify_cp_loss(cp_loss)
side = "W" if mover_white else "B"
print(
logging.info(
f"{ply:>3} {side} {san:<8} "
f"{fmt_eval(played_cp, played_mate):>10} "
f"{fmt_eval(best_cp, best_mate):>9} "
@ -601,7 +602,7 @@ def main():
classification = classify_cp_loss(cp_loss)
side = "W" if mover_white else "B"
print(
logging.info(
f"{ply:>3} {side} {san:<8} "
f"{fmt_eval(played_cp, played_mate):>10} "
f"{fmt_eval(best_cp, best_mate):>9} "

View File

@ -1,3 +1,4 @@
import logging
import os # for: os.getcwd; os.mkdir; os.listdir;
from os import path # for: os.path.abspath
import shutil # for: shutil.move
@ -6,6 +7,8 @@ import shutil # for: shutil.move
# cv2.waitKey; cv2.destroyAllWindows; cv2.IMREAD_COLOR
import cv2
logging.basicConfig(level=logging.INFO)
IMAGE_EXTENSION = (
".bmp",
".dib",
@ -54,7 +57,7 @@ for filename in os.listdir(
if (filename.lower()).endswith(
IMAGE_EXTENSION
): # If the file name ends with image extension
print(filename)
logging.info(filename)
image = cv2.imread(filename, cv2.IMREAD_COLOR)
window_name = filename.split(".")[0]
cv2.namedWindow(window_name) # Window name is the same as image file name

View File

@ -1,7 +1,10 @@
import logging
import random
import tkinter as tk
from tkinter import ttk
logging.basicConfig(level=logging.INFO)
class PokerModifierApp:
def __init__(self):
@ -792,21 +795,21 @@ class PokerModifierApp:
self.debug_mode = self.debug_var.get()
if self.debug_mode:
self.force_endgame_button.pack(side=tk.LEFT, padx=(0, 10))
print("🐛 Debug mode enabled")
logging.debug("Debug mode enabled")
else:
self.force_endgame_button.pack_forget()
self.force_endgame = False
print("🐛 Debug mode disabled")
logging.debug("Debug mode disabled")
def toggle_force_endgame(self):
"""Toggle forced endgame mode for testing."""
self.force_endgame = not self.force_endgame
if self.force_endgame:
self.force_endgame_button.config(text="Stop Force Endgame", bg="#4CAF50")
print("🎯 Forcing endgame modifiers")
logging.debug("Forcing endgame modifiers")
else:
self.force_endgame_button.config(text="Force Endgame", bg="#ff6b6b")
print("🎯 Normal modifier selection restored")
logging.debug("Normal modifier selection restored")
def is_endgame(self):
"""Determine if we're in endgame phase."""
@ -957,7 +960,7 @@ class PokerModifierApp:
if self.debug_mode:
self.force_endgame_button.config(text="Force Endgame", bg="#ff6b6b")
print("🔄 Game reset to initial state")
logging.info("Game reset to initial state")
def add_modifier(self, name, description):
"""Add a new modifier to the list."""
@ -985,13 +988,17 @@ class PokerModifierApp:
def run(self):
"""Start the application."""
print("🃏 Texas Hold'em Modifier App started!")
print("Available methods: app.get_stats(), app.add_modifier(name, description)")
print("Debug features: Toggle debug mode to access force endgame controls")
print(f"Default game length: {self.total_game_rounds} rounds")
logging.info("Texas Hold'em Modifier App started!")
logging.info(
"Available methods: app.get_stats(), app.add_modifier(name, description)"
)
logging.info(
"Debug features: Toggle debug mode to access force endgame controls"
)
logging.info(f"Default game length: {self.total_game_rounds} rounds")
endgame_pct = int(self.endgame_threshold * 100)
endgame_rounds = int(self.total_game_rounds * self.endgame_threshold)
print(f"Endgame threshold: {endgame_pct}% ({endgame_rounds} rounds)")
logging.info(f"Endgame threshold: {endgame_pct}% ({endgame_rounds} rounds)")
self.root.mainloop()

View File

@ -8,7 +8,6 @@ requires-python = ">=3.10"
# RUFF - Extremely fast Python linter and formatter (written in Rust)
# ============================================================================
[tool.ruff]
line-length = 88
target-version = "py310"
# Include all Python files
include = ["*.py", "**/*.py"]
@ -28,14 +27,15 @@ exclude = [
select = ["ALL"]
# Ignores for rules that are too strict for this mixed script repository
ignore = [
# D203 vs D211 conflict - we use D211 (no blank line before class docstring)
"D203", # 1 blank line required before class docstring (conflicts with D211)
# D212 vs D213 conflict - we use D212 (summary on first line after """)
"D213", # Multi-line docstring summary should start at second line (conflicts with D212)
"COM812", # Trailing comma missing (conflicts with formatter)
"ISC001", # Implicit string concatenation (conflicts with formatter)
"ANN101", # Missing type annotation for self (deprecated)
"ANN102", # Missing type annotation for cls (deprecated)
# Relaxed for script-heavy repository
"T201", # print found - these are scripts, print is expected
"D100", # Missing docstring in public module - scripts don't need module docstrings
"D101", # Missing docstring in public class - relaxed
"D102", # Missing docstring in public method - relaxed