mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:23:15 +02:00
fix: replace asserts with combined None checks for ruff S101
This commit is contained in:
parent
97edab318f
commit
9576b20d6c
@ -327,47 +327,12 @@ def append_cases_to_unified_test(
|
||||
|
||||
def _process_single_log(log_path: str) -> int:
|
||||
"""Process a single log file. Returns 0 on success, non-zero otherwise."""
|
||||
try:
|
||||
with open(log_path, encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
except FileNotFoundError:
|
||||
logging.exception(f"Log file not found: {log_path}")
|
||||
return 2
|
||||
|
||||
try:
|
||||
blunders = parse_columns_for_blunders(text)
|
||||
except Exception:
|
||||
logging.exception(f"Error parsing Columns in {os.path.basename(log_path)}")
|
||||
return 2
|
||||
if not blunders:
|
||||
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:
|
||||
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:
|
||||
logging.exception(
|
||||
f"Error converting SAN to UCI in {os.path.basename(log_path)}"
|
||||
)
|
||||
return 2
|
||||
if not cases:
|
||||
logging.warning(
|
||||
f"Failed to reconstruct any blunder positions "
|
||||
f"from PGN: {os.path.basename(log_path)}"
|
||||
)
|
||||
return 1
|
||||
|
||||
base = os.path.basename(log_path)
|
||||
m = re.search(r"game_([A-Za-z0-9]+)\.log$", base)
|
||||
game_id = m.group(1) if m else os.path.splitext(base)[0]
|
||||
result = _parse_and_extract_blunders(log_path, base)
|
||||
if isinstance(result, int):
|
||||
return result # Error code
|
||||
|
||||
cases, game_id = result
|
||||
# Always append to the unified test file
|
||||
unified = os.path.join(
|
||||
os.path.dirname(__file__), "..", "tests", "test_blunders_all.py"
|
||||
@ -381,6 +346,73 @@ def _process_single_log(log_path: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _parse_and_extract_blunders(
|
||||
log_path: str, base: str
|
||||
) -> int | tuple[list[tuple[str, str, str, Blunder]], str]:
|
||||
"""Parse log file and extract blunder cases.
|
||||
|
||||
Returns error code or (cases, game_id).
|
||||
"""
|
||||
text, err = _read_log_file(log_path)
|
||||
if err is not None or text is None:
|
||||
return err if err is not None else 2
|
||||
|
||||
blunders, err = _parse_blunders(text, base)
|
||||
if err is not None or blunders is None:
|
||||
return err if err is not None else 2
|
||||
|
||||
cases, err = _extract_cases(text, blunders, base)
|
||||
if err is not None or cases is None:
|
||||
return err if err is not None else 2
|
||||
|
||||
m = re.search(r"game_([A-Za-z0-9]+)\.log$", base)
|
||||
game_id = m.group(1) if m else os.path.splitext(base)[0]
|
||||
return cases, game_id
|
||||
|
||||
|
||||
def _read_log_file(log_path: str) -> tuple[str | None, int | None]:
|
||||
"""Read log file contents. Returns (text, None) or (None, error_code)."""
|
||||
try:
|
||||
with open(log_path, encoding="utf-8") as fh:
|
||||
return fh.read(), None
|
||||
except FileNotFoundError:
|
||||
logging.exception(f"Log file not found: {log_path}")
|
||||
return None, 2
|
||||
|
||||
|
||||
def _parse_blunders(text: str, base: str) -> tuple[list[Blunder] | None, int | None]:
|
||||
"""Parse blunders from text. Returns (blunders, None) or (None, error_code)."""
|
||||
try:
|
||||
blunders = parse_columns_for_blunders(text)
|
||||
except Exception:
|
||||
logging.exception(f"Error parsing Columns in {base}")
|
||||
return None, 2
|
||||
if not blunders:
|
||||
logging.warning(f"No blunders found in Columns section: {base}")
|
||||
return None, 1
|
||||
return blunders, None
|
||||
|
||||
|
||||
def _extract_cases(
|
||||
text: str, blunders: list[Blunder], base: str
|
||||
) -> tuple[list[tuple[str, str, str, Blunder]] | None, int | None]:
|
||||
"""Extract FEN/UCI cases from PGN. Returns (cases, None) or (None, error_code)."""
|
||||
pgn_text = extract_pgn(text)
|
||||
if not pgn_text:
|
||||
logging.warning(f"No PGN section found: {base}")
|
||||
return None, 1
|
||||
|
||||
try:
|
||||
cases = fen_and_uci_for_blunders(pgn_text, blunders)
|
||||
except Exception:
|
||||
logging.exception(f"Error converting SAN to UCI in {base}")
|
||||
return None, 2
|
||||
if not cases:
|
||||
logging.warning(f"Failed to reconstruct any blunder positions from PGN: {base}")
|
||||
return None, 1
|
||||
return cases, None
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
"""Process log files and generate blunder test cases."""
|
||||
script_dir = os.path.dirname(__file__)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"""Generate random colorful JPEG images with configurable parameters."""
|
||||
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
import logging
|
||||
import os
|
||||
@ -13,24 +14,22 @@ logging.basicConfig(level=logging.INFO)
|
||||
MAX_IMAGE_SIZE = 1000
|
||||
|
||||
|
||||
def generate_bloated_jpeg(
|
||||
size: int,
|
||||
color_list: list[str],
|
||||
block_size: int,
|
||||
output_path: str,
|
||||
quality: int,
|
||||
image_index: int,
|
||||
folder: str,
|
||||
) -> str:
|
||||
"""Generates a random JPEG image with given size, list of colors, and block size.
|
||||
@dataclass
|
||||
class ImageConfig:
|
||||
"""Configuration for generating a bloated JPEG image."""
|
||||
|
||||
size: int
|
||||
color_list: list[str]
|
||||
block_size: int
|
||||
output_path: str
|
||||
quality: int
|
||||
|
||||
|
||||
def generate_bloated_jpeg(config: ImageConfig, image_index: int, folder: str) -> str:
|
||||
"""Generates a random JPEG image with given configuration.
|
||||
|
||||
Args:
|
||||
size: Size of the image (both width and height,
|
||||
must be divisible by block_size).
|
||||
color_list: List of colors in hex format.
|
||||
block_size: Size of the pixel blocks.
|
||||
output_path: Output path for the JPEG image.
|
||||
quality: Quality setting for the JPEG image (0-100).
|
||||
config: Image generation configuration.
|
||||
image_index: Index of the image for unique naming.
|
||||
folder: Folder to save the image.
|
||||
|
||||
@ -38,28 +37,29 @@ def generate_bloated_jpeg(
|
||||
Path to the generated image.
|
||||
"""
|
||||
# Ensure size is divisible by block_size and does not exceed MAX_IMAGE_SIZE
|
||||
if size > MAX_IMAGE_SIZE or size % block_size != 0:
|
||||
if config.size > MAX_IMAGE_SIZE or config.size % config.block_size != 0:
|
||||
msg = (
|
||||
f"Size must be {MAX_IMAGE_SIZE} pixels or less and divisible by block_size"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Create a new image
|
||||
image = Image.new("RGB", (size, size))
|
||||
image = Image.new("RGB", (config.size, config.size))
|
||||
pixels = image.load()
|
||||
|
||||
# Convert hex colors to RGB
|
||||
rgb_colors = [
|
||||
tuple(int(color[i : i + 2], 16) for i in (1, 3, 5)) for color in color_list
|
||||
tuple(int(color[i : i + 2], 16) for i in (1, 3, 5))
|
||||
for color in config.color_list
|
||||
]
|
||||
|
||||
# Fill the image with block_size x block_size pixel squares
|
||||
# of random colors from the list
|
||||
for y in range(0, size, block_size):
|
||||
for x in range(0, size, block_size):
|
||||
for y in range(0, config.size, config.block_size):
|
||||
for x in range(0, config.size, config.block_size):
|
||||
color = random.choice(rgb_colors)
|
||||
for i in range(block_size):
|
||||
for j in range(block_size):
|
||||
for i in range(config.block_size):
|
||||
for j in range(config.block_size):
|
||||
pixels[x + i, y + j] = color
|
||||
|
||||
# Create the folder if it does not exist
|
||||
@ -69,11 +69,12 @@ def generate_bloated_jpeg(
|
||||
# Generate unique output path
|
||||
unique_output_path = os.path.join(
|
||||
folder,
|
||||
f"{os.path.splitext(output_path)[0]}_{image_index}{os.path.splitext(output_path)[1]}",
|
||||
f"{os.path.splitext(config.output_path)[0]}_{image_index}"
|
||||
f"{os.path.splitext(config.output_path)[1]}",
|
||||
)
|
||||
|
||||
# Save the image with specified quality to maximize file size
|
||||
image.save(unique_output_path, "JPEG", quality=quality, optimize=False)
|
||||
image.save(unique_output_path, "JPEG", quality=config.quality, optimize=False)
|
||||
|
||||
return unique_output_path
|
||||
|
||||
@ -149,14 +150,13 @@ if __name__ == "__main__":
|
||||
logging.info(f" Output folder: {folder}")
|
||||
|
||||
# Generate the specified number of images
|
||||
config = ImageConfig(
|
||||
size=args.size,
|
||||
color_list=args.colors,
|
||||
block_size=args.block_size,
|
||||
output_path=args.output_path,
|
||||
quality=args.quality,
|
||||
)
|
||||
for i in range(1, args.num_images + 1):
|
||||
output_path = generate_bloated_jpeg(
|
||||
args.size,
|
||||
args.colors,
|
||||
args.block_size,
|
||||
args.output_path,
|
||||
args.quality,
|
||||
i,
|
||||
folder,
|
||||
)
|
||||
output_path = generate_bloated_jpeg(config, i, folder)
|
||||
logging.info(f"Image {i} saved to {os.path.abspath(output_path)}")
|
||||
|
||||
@ -109,6 +109,16 @@ CP_LOSS_INACCURACY = 99
|
||||
CP_LOSS_MISTAKE = 299
|
||||
|
||||
|
||||
# Centipawn loss thresholds for move classification
|
||||
_CP_LOSS_BANDS = [
|
||||
(CP_LOSS_BEST, "Best"),
|
||||
(CP_LOSS_EXCELLENT, "Excellent"),
|
||||
(CP_LOSS_GOOD, "Good"),
|
||||
(CP_LOSS_INACCURACY, "Inaccuracy"),
|
||||
(CP_LOSS_MISTAKE, "Mistake"),
|
||||
]
|
||||
|
||||
|
||||
def classify_cp_loss(cp_loss: int | None) -> str:
|
||||
"""Classify move quality using Lichess-like centipawn loss bands.
|
||||
|
||||
@ -123,16 +133,9 @@ def classify_cp_loss(cp_loss: int | None) -> str:
|
||||
"""
|
||||
if cp_loss is None:
|
||||
return "Unknown"
|
||||
if cp_loss <= CP_LOSS_BEST:
|
||||
return "Best"
|
||||
if cp_loss <= CP_LOSS_EXCELLENT:
|
||||
return "Excellent"
|
||||
if cp_loss <= CP_LOSS_GOOD:
|
||||
return "Good"
|
||||
if cp_loss <= CP_LOSS_INACCURACY:
|
||||
return "Inaccuracy"
|
||||
if cp_loss <= CP_LOSS_MISTAKE:
|
||||
return "Mistake"
|
||||
for threshold, classification in _CP_LOSS_BANDS:
|
||||
if cp_loss <= threshold:
|
||||
return classification
|
||||
return "Blunder"
|
||||
|
||||
|
||||
|
||||
@ -521,24 +521,34 @@ class PokerModifierApp:
|
||||
|
||||
def setup_gui(self) -> None:
|
||||
"""Create and configure the main GUI window."""
|
||||
# Create main window
|
||||
self._setup_main_window()
|
||||
main_frame = self._create_main_frame()
|
||||
self._create_title(main_frame)
|
||||
self._create_settings_frame(main_frame)
|
||||
self._create_result_display(main_frame)
|
||||
self._create_buttons(main_frame)
|
||||
self._create_statistics_frame(main_frame)
|
||||
|
||||
def _setup_main_window(self) -> None:
|
||||
"""Initialize the main Tk window."""
|
||||
self.root = tk.Tk()
|
||||
self.root.title("🃏 Texas Hold'em Modifier")
|
||||
self.root.geometry("650x750")
|
||||
self.root.configure(bg="#0f4c3a")
|
||||
self.root.resizable(True, True)
|
||||
|
||||
# Configure style
|
||||
style = ttk.Style()
|
||||
style.theme_use("clam")
|
||||
|
||||
# Main container
|
||||
def _create_main_frame(self) -> tk.Frame:
|
||||
"""Create and return the main container frame."""
|
||||
main_frame = tk.Frame(self.root, bg="#0f4c3a", padx=20, pady=20)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||
return main_frame
|
||||
|
||||
# Title
|
||||
def _create_title(self, parent: tk.Frame) -> None:
|
||||
"""Create the title label."""
|
||||
title_label = tk.Label(
|
||||
main_frame,
|
||||
parent,
|
||||
text="🃏 Texas Hold'em Modifier",
|
||||
font=("Arial", 24, "bold"),
|
||||
fg="#ffd700",
|
||||
@ -546,9 +556,13 @@ class PokerModifierApp:
|
||||
)
|
||||
title_label.pack(pady=(0, 20))
|
||||
|
||||
# Settings frame
|
||||
def _create_settings_frame(self, parent: tk.Frame) -> None:
|
||||
"""Create the settings frame.
|
||||
|
||||
Includes probability, debug, and game length controls.
|
||||
"""
|
||||
settings_frame = tk.LabelFrame(
|
||||
main_frame,
|
||||
parent,
|
||||
text="Settings",
|
||||
font=("Arial", 12, "bold"),
|
||||
fg="#ffd700",
|
||||
@ -558,8 +572,13 @@ class PokerModifierApp:
|
||||
)
|
||||
settings_frame.pack(fill=tk.X, pady=(0, 20), padx=10, ipady=10)
|
||||
|
||||
# Probability setting
|
||||
prob_frame = tk.Frame(settings_frame, bg="#1a6b4d")
|
||||
self._create_probability_controls(settings_frame)
|
||||
self._create_debug_controls(settings_frame)
|
||||
self._create_length_controls(settings_frame)
|
||||
|
||||
def _create_probability_controls(self, parent: tk.Widget) -> None:
|
||||
"""Create the probability slider and label."""
|
||||
prob_frame = tk.Frame(parent, bg="#1a6b4d")
|
||||
prob_frame.pack(fill=tk.X, padx=10, pady=5)
|
||||
|
||||
tk.Label(
|
||||
@ -596,11 +615,11 @@ class PokerModifierApp:
|
||||
)
|
||||
self.prob_label.pack(side=tk.RIGHT)
|
||||
|
||||
# Debug controls frame
|
||||
debug_frame = tk.Frame(settings_frame, bg="#1a6b4d")
|
||||
def _create_debug_controls(self, parent: tk.Widget) -> None:
|
||||
"""Create the debug mode checkbox and force endgame button."""
|
||||
debug_frame = tk.Frame(parent, bg="#1a6b4d")
|
||||
debug_frame.pack(fill=tk.X, padx=10, pady=5)
|
||||
|
||||
# Debug mode toggle
|
||||
self.debug_var = tk.BooleanVar(value=False)
|
||||
debug_check = tk.Checkbutton(
|
||||
debug_frame,
|
||||
@ -616,7 +635,6 @@ class PokerModifierApp:
|
||||
)
|
||||
debug_check.pack(side=tk.LEFT, padx=(0, 15))
|
||||
|
||||
# Force endgame button (only visible in debug mode)
|
||||
self.force_endgame_button = tk.Button(
|
||||
debug_frame,
|
||||
text="Force Endgame",
|
||||
@ -629,8 +647,9 @@ class PokerModifierApp:
|
||||
)
|
||||
# Initially hidden
|
||||
|
||||
# Game length setting
|
||||
length_frame = tk.Frame(settings_frame, bg="#1a6b4d")
|
||||
def _create_length_controls(self, parent: tk.Widget) -> None:
|
||||
"""Create the game length slider and label."""
|
||||
length_frame = tk.Frame(parent, bg="#1a6b4d")
|
||||
length_frame.pack(fill=tk.X, padx=10, pady=5)
|
||||
|
||||
tk.Label(
|
||||
@ -667,14 +686,14 @@ class PokerModifierApp:
|
||||
)
|
||||
self.length_label.pack(side=tk.RIGHT)
|
||||
|
||||
# Result display frame
|
||||
def _create_result_display(self, parent: tk.Frame) -> None:
|
||||
"""Create the result display frame."""
|
||||
self.result_frame = tk.Frame(
|
||||
main_frame, bg="#2d2d2d", relief=tk.RIDGE, bd=3, height=150
|
||||
parent, bg="#2d2d2d", relief=tk.RIDGE, bd=3, height=150
|
||||
)
|
||||
self.result_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 20), padx=10)
|
||||
self.result_frame.pack_propagate(False)
|
||||
|
||||
# Initial result text
|
||||
self.result_label = tk.Label(
|
||||
self.result_frame,
|
||||
text="Click 'Start Round' to begin!",
|
||||
@ -686,11 +705,11 @@ class PokerModifierApp:
|
||||
)
|
||||
self.result_label.pack(expand=True, fill=tk.BOTH, padx=20, pady=20)
|
||||
|
||||
# Button frame for Start and Reset
|
||||
button_frame = tk.Frame(main_frame, bg="#0f4c3a")
|
||||
def _create_buttons(self, parent: tk.Frame) -> None:
|
||||
"""Create the start and reset buttons."""
|
||||
button_frame = tk.Frame(parent, bg="#0f4c3a")
|
||||
button_frame.pack(fill=tk.X, pady=(0, 20), padx=10)
|
||||
|
||||
# Start button
|
||||
self.start_button = tk.Button(
|
||||
button_frame,
|
||||
text="Start Round",
|
||||
@ -708,7 +727,6 @@ class PokerModifierApp:
|
||||
side=tk.LEFT, fill=tk.X, expand=True, ipady=10, padx=(0, 5)
|
||||
)
|
||||
|
||||
# Reset button
|
||||
self.reset_button = tk.Button(
|
||||
button_frame,
|
||||
text="Reset Game",
|
||||
@ -724,8 +742,9 @@ class PokerModifierApp:
|
||||
)
|
||||
self.reset_button.pack(side=tk.RIGHT, ipady=10, padx=(5, 0))
|
||||
|
||||
# Statistics frame
|
||||
stats_frame = tk.Frame(main_frame, bg="#0f4c3a")
|
||||
def _create_statistics_frame(self, parent: tk.Frame) -> None:
|
||||
"""Create the statistics display frame with rounds, modifiers, and phase."""
|
||||
stats_frame = tk.Frame(parent, bg="#0f4c3a")
|
||||
stats_frame.pack(fill=tk.X, padx=10)
|
||||
|
||||
# Rounds played
|
||||
|
||||
@ -36,10 +36,6 @@ ignore = [
|
||||
# Relaxed for script-heavy repository
|
||||
"PTH", # Use pathlib instead of os.path - too invasive for existing code
|
||||
"C901", # Function is too complex - many scripts have complex logic
|
||||
"PLR0912", # Too many branches - relaxed for scripts
|
||||
"PLR0915", # Too many statements - relaxed for scripts
|
||||
"PLR0911", # Too many return statements - relaxed
|
||||
"PLR0913", # Too many arguments - relaxed
|
||||
"BLE001", # Blind except - will fix critical ones manually
|
||||
"S603", # subprocess without shell - known pattern
|
||||
"S607", # start-process with partial path - acceptable
|
||||
@ -93,6 +89,12 @@ unfixable = []
|
||||
]
|
||||
"PYTHON/lichess_bot/main.py" = [
|
||||
"PERF203", # Try-except needed for stream/move error handling in loops
|
||||
"PLR0912", # Complex nested game event handling with many branches
|
||||
"PLR0915", # Long function handling complete game lifecycle
|
||||
]
|
||||
"PYTHON/stockfish_analysis/analyze_chess_game.py" = [
|
||||
"PLR0912", # Complex main() with many argument combinations and analysis modes
|
||||
"PLR0915", # Long main() handling complete analysis workflow
|
||||
]
|
||||
"PYTHON/randomize_numbers/random_digits.py" = [
|
||||
"PERF203", # Try-except needed for parsing user input in loop
|
||||
|
||||
Loading…
Reference in New Issue
Block a user