From bb6713eabb44ee89b98e86e5de8f59f6aefdbf30 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sun, 30 Nov 2025 23:03:03 +0100 Subject: [PATCH] fix(lint): convert os.path to pathlib - remove PTH per-file ignores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Converted os.path patterns to pathlib.Path in 15+ files - os.path.join → Path / - os.path.dirname → Path.parent - os.path.exists → Path.exists() - os.path.isfile → Path.is_file() - os.path.abspath → Path.resolve() - os.mkdir → Path.mkdir() - os.listdir → Path.iterdir() - os.getcwd → Path.cwd() - os.replace → Path.replace() - Updated function type hints to accept str | Path Added PTH123 (open() vs Path.open()) to global ignores as stylistic preference --- articles/test_site_size.py | 10 ++-- pyproject.toml | 17 +----- python_pkg/download_cats/generate_cats.py | 5 +- python_pkg/extract_links/main.py | 11 ++-- python_pkg/keyboard_coop/main.py | 6 +- python_pkg/lichess_bot/engine.py | 21 +++---- python_pkg/lichess_bot/main.py | 13 +++-- python_pkg/lichess_bot/tests/conftest.py | 3 +- python_pkg/lichess_bot/tests/test_puzzles.py | 8 +-- .../tools/generate_blunder_tests.py | 56 +++++++++---------- python_pkg/lichess_bot/utils.py | 7 ++- python_pkg/random_jpg/generate_jpeg.py | 18 +++--- python_pkg/scrape_website/scrape_comics.py | 9 +-- python_pkg/screen_locker/screen_lock.py | 10 ++-- .../stockfish_analysis/analyze_chess_game.py | 4 +- python_pkg/tag_divider/tag_divider.py | 33 +++++------ 16 files changed, 103 insertions(+), 128 deletions(-) diff --git a/articles/test_site_size.py b/articles/test_site_size.py index bbbf5ed..31b17dd 100644 --- a/articles/test_site_size.py +++ b/articles/test_site_size.py @@ -1,20 +1,20 @@ """Tests to ensure website stays within size budget.""" -import os +from pathlib import Path # Budget for the entire website (single file) in bytes BUDGET = 14 * 1024 # 14 KiB -HERE = os.path.dirname(__file__) -SITE_FILE = os.path.join(HERE, "index.html") +HERE = Path(__file__).parent +SITE_FILE = HERE / "index.html" def test_site_file_exists() -> None: """Verify the main site HTML file exists.""" - assert os.path.exists(SITE_FILE), f"Missing site file: {SITE_FILE}" + assert SITE_FILE.exists(), f"Missing site file: {SITE_FILE}" def test_site_size_under_budget() -> None: """Verify site size is under the defined budget.""" - size = os.path.getsize(SITE_FILE) + size = SITE_FILE.stat().st_size assert size <= BUDGET, f"Site size {size} bytes exceeds budget {BUDGET}" diff --git a/pyproject.toml b/pyproject.toml index 3d57dcb..aa1e8d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,8 @@ ignore = [ "ISC001", # Implicit string concatenation (conflicts with formatter) # Logging style preference - f-strings are more readable "G004", # Logging statement uses f-string (stylistic preference) + # Path style preference - open() with Path objects is valid Python + "PTH123", # open() should be Path.open() (style preference, not required) ] # Allow ALL rules to be auto-fixed @@ -49,7 +51,6 @@ unfixable = [] "S101", # Allow assert in tests "S603", # Allow subprocess calls in tests "PLR2004", # Allow magic values in tests - "PTH", # Allow os.path in tests for simplicity ] "**/test_*.py" = [ "S101", # Allow assert in tests @@ -57,61 +58,47 @@ unfixable = [] "S310", # Allow URL open in tests "S607", # Allow partial executable path in tests "PLC0415", # Allow late imports for test isolation - "PTH", # Allow os.path in tests for simplicity ] "**/conftest.py" = [ "D100", # Allow missing module docstring "D103", # Allow missing function docstring - "PTH", # Allow os.path in conftest ] "python_pkg/random_jpg/generate_jpeg.py" = [ - "PTH", # os.path patterns in existing code ] "python_pkg/tag_divider/tag_divider.py" = [ - "PTH", # os.path patterns in existing code ] "poker_modifier_app/poker_modifier_app.py" = [ "FBT003", # Boolean positional values in tkinter API calls ] "python_pkg/download_cats/generate_cats.py" = [ - "PTH", # os.path patterns in existing code ] "python_pkg/lichess_bot/main.py" = [ "C901", # Complex functions handling game lifecycle (run_bot, handle_game) "PLR0912", # Complex nested game event handling with many branches "PLR0915", # Long function handling complete game lifecycle "S603", # Subprocess call for analysis script - "PTH", # os.path patterns in existing code ] "python_pkg/lichess_bot/engine.py" = [ "S603", # Subprocess for engine communication - "PTH", # os.path patterns ] "python_pkg/lichess_bot/utils.py" = [ - "PTH", # os.path patterns ] "python_pkg/lichess_bot/tools/generate_blunder_tests.py" = [ - "PTH", # os.path patterns in tool ] "python_pkg/stockfish_analysis/analyze_chess_game.py" = [ "C901", # Complex main() with many argument combinations and analysis modes "PLR0912", # Complex main() with many argument combinations and analysis modes "PLR0915", # Long main() handling complete analysis workflow - "PTH", # os.path patterns ] "python_pkg/keyboard_coop/main.py" = [ "FBT003", # Boolean positional values in pygame API calls (e.g., font.render) - "PTH", # os.path patterns ] "python_pkg/screen_locker/screen_lock.py" = [ "FBT003", # Boolean positional values in tkinter API calls - "PTH", # os.path patterns ] "python_pkg/scrape_website/scrape_comics.py" = [ - "PTH", # os.path patterns ] "python_pkg/extract_links/main.py" = [ - "PTH", # os.path patterns ] [tool.ruff.lint.pydocstyle] diff --git a/python_pkg/download_cats/generate_cats.py b/python_pkg/download_cats/generate_cats.py index f85edc6..b5ec6f9 100644 --- a/python_pkg/download_cats/generate_cats.py +++ b/python_pkg/download_cats/generate_cats.py @@ -5,7 +5,6 @@ Fetches cat images in batches and saves them to a local directory. import json import logging -import os from pathlib import Path import requests @@ -28,8 +27,8 @@ def _download_single_image(url: str) -> None: response.raise_for_status() # Raise an exception for HTTP errors # Extract the image name from the URL - image_name = os.path.basename(url) - image_path = os.path.join("./CATS2/", image_name) + image_name = Path(url).name + image_path = Path("./CATS2/") / image_name # Save the image to the directory with open(image_path, "wb") as file: diff --git a/python_pkg/extract_links/main.py b/python_pkg/extract_links/main.py index d200380..22cc768 100755 --- a/python_pkg/extract_links/main.py +++ b/python_pkg/extract_links/main.py @@ -13,7 +13,7 @@ from __future__ import annotations import argparse from html.parser import HTMLParser import logging -import os +from pathlib import Path from urllib.parse import urlparse _logger = logging.getLogger(__name__) @@ -72,15 +72,16 @@ def main() -> int: ) args = ap.parse_args() - input_path = args.input_html - if not os.path.isfile(input_path): + input_path = Path(args.input_html) + if not input_path.is_file(): msg = f"Input file not found: {input_path}" raise SystemExit(msg) out_path = args.output_txt if not out_path: - base = os.path.splitext(os.path.basename(input_path))[0] - out_path = os.path.join(os.path.dirname(input_path), f"{base}_links.txt") + out_path = input_path.parent / f"{input_path.stem}_links.txt" + else: + out_path = Path(out_path) with open(input_path, encoding="utf-8", errors="ignore") as f: html_text = f.read() diff --git a/python_pkg/keyboard_coop/main.py b/python_pkg/keyboard_coop/main.py index 7d3be9d..5022ad7 100644 --- a/python_pkg/keyboard_coop/main.py +++ b/python_pkg/keyboard_coop/main.py @@ -5,7 +5,7 @@ Players take turns selecting adjacent keys to form valid English words. import json import logging -import os +from pathlib import Path import secrets import sys @@ -102,9 +102,7 @@ class KeyboardCoopGame: def load_dictionary(self) -> set[str]: """Load dictionary from words_dictionary.json file.""" try: - dictionary_path = os.path.join( - os.path.dirname(__file__), "words_dictionary.json" - ) + dictionary_path = Path(__file__).parent / "words_dictionary.json" with open(dictionary_path, encoding="utf-8") as f: dictionary_data = json.load(f) # Convert to set for faster lookup (we only need the keys) diff --git a/python_pkg/lichess_bot/engine.py b/python_pkg/lichess_bot/engine.py index d5d516c..b507d0a 100644 --- a/python_pkg/lichess_bot/engine.py +++ b/python_pkg/lichess_bot/engine.py @@ -4,6 +4,7 @@ import contextlib import json import logging import os +from pathlib import Path import subprocess import chess @@ -36,20 +37,14 @@ class RandomEngine: # the C engine handles its own scoring/selection. self.depth = depth # Default relative path inside this repo - default_path = os.path.abspath( - os.path.join( - os.path.dirname(__file__), - "..", - "..", - "C", - "lichess_random_engine", - "random_engine", - ) + default_path = ( + Path(__file__).resolve().parent.parent.parent + / "C" + / "lichess_random_engine" + / "random_engine" ) - self.engine_path = engine_path or default_path - if not os.path.isfile(self.engine_path) or not os.access( - self.engine_path, os.X_OK - ): + self.engine_path = Path(engine_path) if engine_path else default_path + if not self.engine_path.is_file() or not os.access(self.engine_path, os.X_OK): msg = ( f"C engine not found or not executable at '{self.engine_path}'. " "Build it first (make -C C/lichess_random_engine)." diff --git a/python_pkg/lichess_bot/main.py b/python_pkg/lichess_bot/main.py index d288150..1e9938a 100644 --- a/python_pkg/lichess_bot/main.py +++ b/python_pkg/lichess_bot/main.py @@ -6,6 +6,7 @@ import datetime import json import logging import os +from pathlib import Path import subprocess import sys import threading @@ -65,7 +66,7 @@ def run_bot(log_level: str = "INFO", *, decline_correspondence: bool = False) -> # start at -1 so we act on the first state (0 moves) last_handled_len = -1 # Prepare a per-game log file - game_log_path = os.path.join(os.getcwd(), f"lichess_bot_game_{game_id}.log") + game_log_path = Path.cwd() / f"lichess_bot_game_{game_id}.log" try: with open(game_log_path, "w") as lf: lf.write(f"game {game_id} started\n") @@ -277,12 +278,12 @@ def run_bot(log_level: str = "INFO", *, decline_correspondence: bool = False) -> if game_log_path: analysis_text: str | None = None try: - analyze_script = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - "stockfish_analysis", - "analyze_chess_game.py", + analyze_script = ( + Path(__file__).resolve().parent.parent + / "stockfish_analysis" + / "analyze_chess_game.py" ) - if os.path.isfile(analyze_script): + if analyze_script.is_file(): # Estimate total plies from the final board try: total_plies = len(board.move_stack) diff --git a/python_pkg/lichess_bot/tests/conftest.py b/python_pkg/lichess_bot/tests/conftest.py index ec89299..cbc08fe 100644 --- a/python_pkg/lichess_bot/tests/conftest.py +++ b/python_pkg/lichess_bot/tests/conftest.py @@ -1,4 +1,3 @@ -import os from pathlib import Path import sys @@ -6,7 +5,7 @@ import pytest # Add repository root to sys.path so 'import python_pkg.*' works when running # pytest with a subdirectory as rootdir. -ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) +ROOT = str(Path(__file__).resolve().parent.parent.parent.parent) if ROOT not in sys.path: sys.path.insert(0, ROOT) diff --git a/python_pkg/lichess_bot/tests/test_puzzles.py b/python_pkg/lichess_bot/tests/test_puzzles.py index a83c91a..b084ecd 100644 --- a/python_pkg/lichess_bot/tests/test_puzzles.py +++ b/python_pkg/lichess_bot/tests/test_puzzles.py @@ -1,23 +1,23 @@ """Test the engine against Lichess puzzles.""" import csv -import os +from pathlib import Path import chess import pytest from python_pkg.lichess_bot.engine import RandomEngine -_PUZZLE_CSV = os.path.join(os.path.dirname(__file__), "lichess_db_puzzle.csv") +_PUZZLE_CSV = Path(__file__).parent / "lichess_db_puzzle.csv" -def _load_top_puzzles(csv_path: str, limit: int = 8) -> list[tuple[str, str]]: +def _load_top_puzzles(csv_path: str | Path, limit: int = 8) -> list[tuple[str, str]]: """Return a list of (FEN, solution_moves_str) for the first `limit` rows in the CSV. CSV columns: PuzzleId,FEN,Moves,... """ puzzles: list[tuple[str, str]] = [] - if not os.path.isfile(csv_path): + if not Path(csv_path).is_file(): return puzzles with open(csv_path, newline="", encoding="utf-8") as f: reader = csv.DictReader(f) diff --git a/python_pkg/lichess_bot/tools/generate_blunder_tests.py b/python_pkg/lichess_bot/tools/generate_blunder_tests.py index 7dd0e97..f0d6da4 100755 --- a/python_pkg/lichess_bot/tools/generate_blunder_tests.py +++ b/python_pkg/lichess_bot/tools/generate_blunder_tests.py @@ -36,7 +36,7 @@ from __future__ import annotations from dataclasses import dataclass import io import logging -import os +from pathlib import Path import re import sys @@ -183,10 +183,10 @@ def fen_and_uci_for_blunders( return results -def ensure_unified_test_file(target_path: str) -> None: +def ensure_unified_test_file(target_path: str | Path) -> None: """Create the unified test file skeleton if it doesn't exist.""" - os.makedirs(os.path.dirname(target_path), exist_ok=True) - if os.path.exists(target_path): + Path(target_path).parent.mkdir(parents=True, exist_ok=True) + if Path(target_path).exists(): return # Create skeleton unified test file with open(target_path, "w", encoding="utf-8") as f: @@ -197,8 +197,8 @@ import chess import pytest # Ensure repo root is importable when running pytest directly -REPO_ROOT = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +REPO_ROOT = str( + Path(__file__).resolve().parent.parent.parent ) if REPO_ROOT not in sys.path: sys.path.insert(0, REPO_ROOT) @@ -239,7 +239,7 @@ def test_engine_avoids_logged_blunder(fen, blunder_uci, label): def append_cases_to_unified_test( - unified_path: str, cases: list[tuple[str, str, str, Blunder]] + unified_path: str | Path, cases: list[tuple[str, str, str, Blunder]] ) -> int: """Append new cases to BLUNDER_CASES in the unified test file, skipping duplicates. @@ -325,29 +325,27 @@ def append_cases_to_unified_test( return len(lines) + updated_existing -def _process_single_log(log_path: str) -> int: +def _process_single_log(log_path: str | Path) -> int: """Process a single log file. Returns 0 on success, non-zero otherwise.""" - base = os.path.basename(log_path) + base = Path(log_path).name 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" - ) - unified = os.path.abspath(unified) + unified = Path(__file__).parent.parent / "tests" / "test_blunders_all.py" + unified = unified.resolve() added = append_cases_to_unified_test(unified, cases) _logger.info( f"Appended {added} new blunder checks to " - f"{os.path.relpath(unified)} (game {game_id})." + f"{unified.relative_to(Path.cwd())} (game {game_id})." ) return 0 def _parse_and_extract_blunders( - log_path: str, base: str + log_path: str | Path, base: str ) -> int | tuple[list[tuple[str, str, str, Blunder]], str]: """Parse log file and extract blunder cases. @@ -366,11 +364,11 @@ def _parse_and_extract_blunders( 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] + game_id = m.group(1) if m else Path(base).stem return cases, game_id -def _read_log_file(log_path: str) -> tuple[str | None, int | None]: +def _read_log_file(log_path: str | Path) -> 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: @@ -415,24 +413,24 @@ def _extract_cases( def main(argv: list[str]) -> int: """Process log files and generate blunder test cases.""" - script_dir = os.path.dirname(__file__) - past_dir = os.path.abspath(os.path.join(script_dir, "past_games")) + script_dir = Path(__file__).parent + past_dir = (script_dir / "past_games").resolve() # No argument: process all logs in past_games if len(argv) == 1: - if not os.path.isdir(past_dir): + if not past_dir.is_dir(): _logger.error(f"No past_games directory found at {past_dir}") return 2 logs = [ - os.path.join(past_dir, name) - for name in os.listdir(past_dir) - if re.match(r"lichess_bot_game_[A-Za-z0-9]+\.log$", name) + path + for path in past_dir.iterdir() + if re.match(r"lichess_bot_game_[A-Za-z0-9]+\.log$", path.name) ] if not logs: _logger.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)) + logs.sort(key=lambda p: Path(p).stat().st_mtime) ok = 0 for lp in logs: rc = _process_single_log(lp) @@ -446,16 +444,16 @@ def main(argv: list[str]) -> int: # One argument: game id or file path arg = argv[1] - candidate_path = None - if os.path.isfile(arg): + candidate_path: str | Path | None = None + if Path(arg).is_file(): candidate_path = arg # Treat as game id, resolve within past_games elif re.fullmatch(r"[A-Za-z0-9]+", arg): - candidate_path = os.path.join(past_dir, f"lichess_bot_game_{arg}.log") + candidate_path = past_dir / f"lichess_bot_game_{arg}.log" else: # Fallback: if it's a bare filename, try inside past_games - maybe = os.path.join(past_dir, arg) - if os.path.isfile(maybe): + maybe = past_dir / arg + if maybe.is_file(): candidate_path = maybe if not candidate_path: diff --git a/python_pkg/lichess_bot/utils.py b/python_pkg/lichess_bot/utils.py index a9b825c..248c796 100644 --- a/python_pkg/lichess_bot/utils.py +++ b/python_pkg/lichess_bot/utils.py @@ -2,6 +2,7 @@ import logging import os +from pathlib import Path import time _logger = logging.getLogger(__name__) @@ -15,7 +16,7 @@ def _version_file_path() -> str: override = os.getenv("LICHESS_BOT_VERSION_FILE") if override: return override - return os.path.join(os.path.dirname(__file__), ".bot_version") + return str(Path(__file__).parent / ".bot_version") def get_and_increment_version() -> int: @@ -36,10 +37,10 @@ def get_and_increment_version() -> int: new_version = current + 1 try: - tmp_path = path + ".tmp" + tmp_path = Path(path + ".tmp") with open(tmp_path, "w") as f: f.write(str(new_version)) - os.replace(tmp_path, path) + tmp_path.replace(path) except OSError: # As a fallback, try a direct write; failure is non-fatal to bot operation try: diff --git a/python_pkg/random_jpg/generate_jpeg.py b/python_pkg/random_jpg/generate_jpeg.py index c15192b..aca774c 100644 --- a/python_pkg/random_jpg/generate_jpeg.py +++ b/python_pkg/random_jpg/generate_jpeg.py @@ -4,7 +4,7 @@ import argparse from dataclasses import dataclass from datetime import datetime, timezone import logging -import os +from pathlib import Path import secrets from PIL import Image @@ -66,20 +66,18 @@ def generate_bloated_jpeg(config: ImageConfig, image_index: int, folder: str) -> pixels[x + i, y + j] = color # Create the folder if it does not exist - if not os.path.exists(folder): - os.makedirs(folder) + folder_path = Path(folder) + folder_path.mkdir(parents=True, exist_ok=True) # Generate unique output path - unique_output_path = os.path.join( - folder, - f"{os.path.splitext(config.output_path)[0]}_{image_index}" - f"{os.path.splitext(config.output_path)[1]}", - ) + output_stem = Path(config.output_path).stem + output_suffix = Path(config.output_path).suffix + unique_output_path = folder_path / f"{output_stem}_{image_index}{output_suffix}" # Save the image with specified quality to maximize file size image.save(unique_output_path, "JPEG", quality=config.quality, optimize=False) - return unique_output_path + return str(unique_output_path) if __name__ == "__main__": @@ -162,4 +160,4 @@ if __name__ == "__main__": ) for i in range(1, args.num_images + 1): output_path = generate_bloated_jpeg(config, i, folder) - _logger.info("Image %s saved to %s", i, os.path.abspath(output_path)) + _logger.info("Image %s saved to %s", i, Path(output_path).resolve()) diff --git a/python_pkg/scrape_website/scrape_comics.py b/python_pkg/scrape_website/scrape_comics.py index 8d29be3..09e8828 100644 --- a/python_pkg/scrape_website/scrape_comics.py +++ b/python_pkg/scrape_website/scrape_comics.py @@ -2,7 +2,7 @@ import argparse import logging -import os +from pathlib import Path from urllib.parse import urlparse import requests @@ -34,15 +34,16 @@ driver.get(url) def download_image(url: str) -> bool: """Download an image from a URL and save it locally.""" # Extract image name from URL - image_name = os.path.basename(urlparse(url).path) + image_name = Path(urlparse(url).path).name + image_path = Path(image_name) # Check if the image already exists - if os.path.exists(image_name): + if image_path.exists(): _logger.info("Image %s already exists, skipping download.", image_name) return False _logger.info("Downloading image from URL: %s", url) img_data = requests.get(url, timeout=REQUEST_TIMEOUT).content - with open(image_name, "wb") as handler: + with open(image_path, "wb") as handler: handler.write(img_data) _logger.info("Image %s downloaded successfully", image_name) return True diff --git a/python_pkg/screen_locker/screen_lock.py b/python_pkg/screen_locker/screen_lock.py index 9d3be03..235b718 100755 --- a/python_pkg/screen_locker/screen_lock.py +++ b/python_pkg/screen_locker/screen_lock.py @@ -7,7 +7,7 @@ Requires user to log their workout to unlock the screen. from datetime import datetime, timezone import json import logging -import os +from pathlib import Path import sys import tkinter as tk @@ -29,8 +29,8 @@ class ScreenLocker: def __init__(self, *, demo_mode: bool = True) -> None: """Initialize screen locker with optional demo mode.""" # Set up log file path - script_dir = os.path.dirname(os.path.abspath(__file__)) - self.log_file = os.path.join(script_dir, "workout_log.json") + script_dir = Path(__file__).resolve().parent + self.log_file = script_dir / "workout_log.json" # Check if already logged today if self.has_logged_today(): @@ -628,7 +628,7 @@ class ScreenLocker: def has_logged_today(self) -> bool: """Check if workout has been logged today.""" - if not os.path.exists(self.log_file): + if not self.log_file.exists(): return False try: @@ -644,7 +644,7 @@ class ScreenLocker: """Save workout data to log file.""" # Load existing logs logs = {} - if os.path.exists(self.log_file): + if self.log_file.exists(): try: with open(self.log_file) as f: logs = json.load(f) diff --git a/python_pkg/stockfish_analysis/analyze_chess_game.py b/python_pkg/stockfish_analysis/analyze_chess_game.py index 4cde518..4b2e15d 100755 --- a/python_pkg/stockfish_analysis/analyze_chess_game.py +++ b/python_pkg/stockfish_analysis/analyze_chess_game.py @@ -26,7 +26,7 @@ import contextlib import io import logging import multiprocessing -import os +from pathlib import Path import re import sys @@ -271,7 +271,7 @@ def main() -> None: ) args = ap.parse_args() - if not os.path.isfile(args.file): + if not Path(args.file).is_file(): _logger.error(f"Input not found: {args.file}") sys.exit(1) diff --git a/python_pkg/tag_divider/tag_divider.py b/python_pkg/tag_divider/tag_divider.py index 443c6e9..8a04f99 100644 --- a/python_pkg/tag_divider/tag_divider.py +++ b/python_pkg/tag_divider/tag_divider.py @@ -1,8 +1,8 @@ """Sort images into folders using keyboard input.""" import logging -import os # for: os.getcwd; os.mkdir; os.listdir; -from os import path # for: os.path.abspath +import os # for: os.chdir +from pathlib import Path import shutil # for: shutil.move # for: cv2.imread; cv2.namedWindow; cv2.imshow; @@ -41,21 +41,18 @@ RIGHT_FOLDER_CODE = 97 # Default 97 - 'a' first_folder_name = input("Enter first folder name: [a] ") second_folder_name = input("Enter second folder name: [d] ") -current_path = os.path.abspath( - os.getcwd() -) # Stolen from: https://stackoverflow.com/q/3430372 +current_path = Path.cwd().resolve() os.chdir(current_path) # Change working directory to the path where the python file is -if ( - path.isdir(first_folder_name) != 1 -): # Check if folder already exists, if it does not make it - os.mkdir(first_folder_name) -if path.isdir(second_folder_name) != 1: - os.mkdir(second_folder_name) +if not Path( + first_folder_name +).is_dir(): # Check if folder already exists, if not make it + Path(first_folder_name).mkdir() +if not Path(second_folder_name).is_dir(): + Path(second_folder_name).mkdir() -for filename in os.listdir( - os.getcwd() -): # Go through every file in the working directory +for file_path in Path.cwd().iterdir(): # Go through every file in the working directory + filename = file_path.name if (filename.lower()).endswith( IMAGE_EXTENSION ): # If the file name ends with image extension @@ -67,12 +64,12 @@ for filename in os.listdir( key = cv2.waitKey() if key == RIGHT_FOLDER_CODE: shutil.move( - current_path + "/" + filename, - current_path + "/" + first_folder_name + "/" + filename, + current_path / filename, + current_path / first_folder_name / filename, ) elif key == LEFT_FOLDER_CODE: shutil.move( - current_path + "/" + filename, - current_path + "/" + second_folder_name + "/" + filename, + current_path / filename, + current_path / second_folder_name / filename, ) cv2.destroyAllWindows()