From 9773f55b381693c0f36832c5ef44a4d645e1c740 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Sat, 10 May 2025 13:56:43 +0200 Subject: [PATCH] Use pathlib more --- b_asic/GUI/drag_button.py | 13 +++--- b_asic/GUI/main_window.py | 24 +++++------ b_asic/codegen/testbench/test.py | 8 ++-- b_asic/logger.py | 4 +- b_asic/resources.py | 5 ++- b_asic/save_load_structure.py | 3 +- b_asic/scheduler_gui/compile.py | 6 +-- b_asic/scheduler_gui/main_window.py | 23 +++++----- test/conftest.py | 11 ++--- test/unit/test_sfg.py | 65 +++++++++++++++-------------- 10 files changed, 84 insertions(+), 78 deletions(-) diff --git a/b_asic/GUI/drag_button.py b/b_asic/GUI/drag_button.py index 48179611..c83325ec 100644 --- a/b_asic/GUI/drag_button.py +++ b/b_asic/GUI/drag_button.py @@ -4,7 +4,7 @@ B-ASIC Drag Button Module. Contains a GUI class for drag buttons. """ -import os.path +from pathlib import Path from typing import TYPE_CHECKING from qtpy.QtCore import QSize, Qt, Signal @@ -177,13 +177,12 @@ class DragButton(QPushButton): " border-style: solid; border-color: black;" " border-width: 2px" ) - path_to_image = os.path.join( - os.path.dirname(__file__), - "operation_icons", - f"{self.operation.type_name().lower()}" - f"{'_grey.png' if self.pressed else '.png'}", + path_to_image = ( + Path(__file__).parent + / "operation_icons" + / f"{self.operation.type_name().lower()}{'_grey' if self.pressed else ''}.png" ) - self.setIcon(QIcon(path_to_image)) + self.setIcon(QIcon(str(path_to_image))) self.setIconSize(QSize(MINBUTTONSIZE, MINBUTTONSIZE)) def is_flipped(self) -> bool: diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py index 126cdd36..10280e73 100644 --- a/b_asic/GUI/main_window.py +++ b/b_asic/GUI/main_window.py @@ -6,11 +6,11 @@ This file opens the main SFG editor window of the GUI for B-ASIC when run. import importlib.util import logging -import os import sys import webbrowser from collections import deque from collections.abc import Sequence +from pathlib import Path from types import ModuleType from typing import TYPE_CHECKING, cast @@ -285,7 +285,7 @@ class SFGMainWindow(QMainWindow): ) try: - with open(module, "w+") as file_obj: + with Path(module).open("w+") as file_obj: file_obj.write( sfg_to_python(sfg, suffix=f"positions = {operation_positions}") ) @@ -710,18 +710,18 @@ class SFGMainWindow(QMainWindow): attr_button.add_ports() self._ports[attr_button] = attr_button.port_list - icon_path = os.path.join( - os.path.dirname(__file__), - "operation_icons", - f"{op.type_name().lower()}.png", + icon_path = ( + Path(__file__).parent + / "operation_icons" + / f"{op.type_name().lower()}.png" ) - if not os.path.exists(icon_path): - icon_path = os.path.join( - os.path.dirname(__file__), - "operation_icons", - "custom_operation.png", + + print(icon_path) + if not Path(icon_path).exists(): + icon_path = ( + Path(__file__).parent / "operation_icons" / "custom_operation.png" ) - attr_button.setIcon(QIcon(icon_path)) + attr_button.setIcon(QIcon(str(icon_path))) attr_button.setIconSize(QSize(MINBUTTONSIZE, MINBUTTONSIZE)) attr_button.setToolTip("No SFG") attr_button.setStyleSheet( diff --git a/b_asic/codegen/testbench/test.py b/b_asic/codegen/testbench/test.py index 3e673879..708149e1 100755 --- a/b_asic/codegen/testbench/test.py +++ b/b_asic/codegen/testbench/test.py @@ -3,15 +3,17 @@ B-ASIC Codegen Testing Module. """ #!/usr/bin/env python3 -from os.path import abspath, dirname +from pathlib import Path from sys import argv from vunit import VUnit # Absolute path of the testbench directory -testbench_path = dirname(abspath(__file__)) +testbench_path = Path(__file__).resolve().parent -vu = VUnit.from_argv(argv=["--output-path", f"{testbench_path}/vunit_out"] + argv[1:]) +vu = VUnit.from_argv( + argv=["--output-path", str(testbench_path / "vunit_out")] + argv[1:] +) lib = vu.add_library("lib") lib.add_source_files( diff --git a/b_asic/logger.py b/b_asic/logger.py index 986c1d50..92186852 100644 --- a/b_asic/logger.py +++ b/b_asic/logger.py @@ -49,9 +49,9 @@ To log uncaught exceptions, implement the following in your program. """ import logging -import os import sys from logging import Logger +from pathlib import Path from types import TracebackType from typing import Literal @@ -113,7 +113,7 @@ def getLogger( if logger.name == "scheduler-gui.log": logger.info( "Running: %s %s", - os.path.basename(sys.argv[0]), + Path(sys.argv[0]).name, " ".join(sys.argv[1:]), ) diff --git a/b_asic/resources.py b/b_asic/resources.py index e8531cb3..a7e1fb6d 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -12,6 +12,7 @@ from collections import Counter, defaultdict from collections.abc import Iterable, Iterator from functools import reduce from math import floor, log2 +from pathlib import Path from typing import TYPE_CHECKING, Literal, TypeVar, Union import matplotlib.pyplot as plt @@ -2211,7 +2212,7 @@ class ProcessCollection: "both or none of adr_mux_size and adr_pipe_depth needs to be set" ) - with open(filename, "w") as f: + with Path(filename).open("w") as f: from b_asic.codegen.vhdl import architecture, common, entity common.b_asic_preamble(f) @@ -2335,7 +2336,7 @@ class ProcessCollection: # Create the forward-backward table forward_backward_table = _ForwardBackwardTable(self) - with open(filename, "w") as f: + with Path(filename).open("w") as f: from b_asic.codegen.vhdl import architecture, common, entity common.b_asic_preamble(f) diff --git a/b_asic/save_load_structure.py b/b_asic/save_load_structure.py index 34d84498..b283a8d5 100644 --- a/b_asic/save_load_structure.py +++ b/b_asic/save_load_structure.py @@ -7,6 +7,7 @@ stored as files. from datetime import datetime from inspect import signature +from pathlib import Path from typing import cast from b_asic.graph_component import GraphComponent @@ -152,7 +153,7 @@ def python_to_sfg(path: str) -> tuple[SFG, dict[str, tuple[int, int]]]: """ local_vars = {} - with open(path) as file: + with Path(path).open() as file: code = compile(file.read(), path, "exec") exec(code, globals(), local_vars) diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 632ca4e1..17d8380d 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -53,11 +53,11 @@ def replace_qt_bindings(filename: str) -> None: filename : str The name of the file to replace bindings in. """ - with open(f"{filename}") as file: + with Path(filename).open() as file: filedata = file.read() filedata = filedata.replace("from PyQt6", "from qtpy") filedata = filedata.replace("from PySide6", "from qtpy") - with open(f"{filename}", "w") as file: + with Path(filename).open("w") as file: file.write(filedata) @@ -159,7 +159,7 @@ def compile_ui(*filenames: str) -> None: if uic.PYQT6: from qtpy.uic import compileUi - with open(outfile, "w") as ofile: + with Path(outfile).open("w") as ofile: compileUi(filename, ofile) elif uic.PYSIDE6: diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index ef12541d..d599eca5 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -15,6 +15,7 @@ import sys import webbrowser from collections import defaultdict, deque from importlib.machinery import SourceFileLoader +from pathlib import Path from typing import TYPE_CHECKING, ClassVar, cast, overload # Qt/qtpy @@ -119,6 +120,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): _changed_operation_colors: dict[str, QColor] _recent_files_actions: list[QAction] _recent_file_paths: deque[str] + _file_name: str | None def __init__(self) -> None: """Initialize Scheduler-GUI.""" @@ -331,9 +333,9 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0], str, ) - if not os.path.exists(last_file): # if filename does not exist - last_file = os.path.dirname(last_file) + "/" - if not os.path.exists(last_file): # if path does not exist + if not Path(last_file).exists(): # if filename does not exist + last_file = Path(last_file).parent + if not last_file.exists(): # if path does not exist last_file = QStandardPaths.standardLocations( QStandardPaths.HomeLocation )[0] @@ -381,16 +383,15 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): ) if not schedule_obj_list: # return if no Schedule objects in script + filename = Path(abs_path_filename).name QMessageBox.warning( self, self.tr("File not found"), - self.tr("Cannot find any Schedule object in file {}").format( - os.path.basename(abs_path_filename) - ), + self.tr("Cannot find any Schedule object in file {}").format(filename), ) log.info( "Cannot find any Schedule object in file %s", - os.path.basename(abs_path_filename), + filename, ) del module return @@ -481,7 +482,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): self.save_as() return self._schedule._sfg._graph_id_generator = None - with open(self._file_name, "wb") as f: + with Path(self._file_name).open("wb") as f: pickle.dump(self._schedule, f) self._add_recent_file(self._file_name) self.update_statusbar(self.tr("Schedule saved successfully")) @@ -505,7 +506,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): filename += ".bsc" self._file_name = filename self._schedule._sfg._graph_id_generator = None - with open(self._file_name, "wb") as f: + with Path(self._file_name).open("wb") as f: pickle.dump(self._schedule, f) self._add_recent_file(self._file_name) self.update_statusbar(self.tr("Schedule saved successfully")) @@ -536,7 +537,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): self._file_name = abs_path_filename self._add_recent_file(abs_path_filename) - with open(self._file_name, "rb") as f: + with Path(self._file_name).open("rb") as f: schedule = pickle.load(f) self.open(schedule) settings = QSettings() @@ -676,7 +677,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): if not hide_dialog: settings.setValue("scheduler/hide_exit_dialog", checkbox.isChecked()) self._write_settings() - log.info("Exit: %s", os.path.basename(__file__)) + log.info("Exit: %s", Path(__file__).name) if self._ports_accesses_for_storage: self._ports_accesses_for_storage.close() if self._execution_time_for_variables: diff --git a/test/conftest.py b/test/conftest.py index 0bc4ae71..e296b3c2 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,5 +1,5 @@ -import os import shutil +from pathlib import Path import pytest @@ -14,10 +14,11 @@ from test.fixtures.signal_flow_graph import * @pytest.fixture def datadir(tmpdir, request): - filename = request.module.__file__ - test_dir, _ = os.path.splitext(filename) + filepath = Path(request.module.__file__) - if os.path.isdir(test_dir): - shutil.copytree(test_dir, str(tmpdir), dirs_exist_ok=True) + test_dir = filepath.parent / filepath.stem + + if test_dir.is_dir(): + shutil.copytree(test_dir, Path(tmpdir), dirs_exist_ok=True) return tmpdir diff --git a/test/unit/test_sfg.py b/test/unit/test_sfg.py index 8f29cb71..4ba1f3a4 100644 --- a/test/unit/test_sfg.py +++ b/test/unit/test_sfg.py @@ -5,7 +5,7 @@ import re import string import sys from collections import Counter -from os import path, remove +from pathlib import Path import numpy as np import pytest @@ -1090,82 +1090,83 @@ class TestRemove: class TestSaveLoadSFG: - def get_path(self, existing=False): - path_ = "".join(random.choices(string.ascii_uppercase, k=4)) + ".py" - while path.exists(path_) if not existing else not path.exists(path_): - path_ = "".join(random.choices(string.ascii_uppercase, k=4)) + ".py" + # TODO: Rewrite to use TempDir/TempFileF + def get_path(self, existing=False) -> Path: + path = Path("".join(random.choices(string.ascii_uppercase, k=4)) + ".py") + while path.exists() if not existing else not path.exists(): + path = Path("".join(random.choices(string.ascii_uppercase, k=4)) + ".py") - return path_ + return path def test_save_simple_sfg(self, sfg_simple_filter): result = sfg_to_python(sfg_simple_filter) - path_ = self.get_path() + path = self.get_path() - assert not path.exists(path_) - with open(path_, "w") as file_obj: + assert not path.exists() + with path.open("w") as file_obj: file_obj.write(result) - assert path.exists(path_) + assert path.exists() - with open(path_) as file_obj: + with path.open() as file_obj: assert file_obj.read() == result - remove(path_) + path.unlink() def test_save_complex_sfg(self, precedence_sfg_delays_and_constants): result = sfg_to_python(precedence_sfg_delays_and_constants) - path_ = self.get_path() + path = self.get_path() - assert not path.exists(path_) - with open(path_, "w") as file_obj: + assert not path.exists() + with path.open("w") as file_obj: file_obj.write(result) - assert path.exists(path_) + assert path.exists() - with open(path_) as file_obj: + with path.open() as file_obj: assert file_obj.read() == result - remove(path_) + path.unlink() def test_load_simple_sfg(self, sfg_simple_filter): result = sfg_to_python(sfg_simple_filter) - path_ = self.get_path() + path = self.get_path() - assert not path.exists(path_) - with open(path_, "w") as file_obj: + assert not path.exists() + with path.open("w") as file_obj: file_obj.write(result) - assert path.exists(path_) + assert path.exists() - simple_filter_, _ = python_to_sfg(path_) + simple_filter_, _ = python_to_sfg(path) assert str(sfg_simple_filter) == str(simple_filter_) assert sfg_simple_filter.evaluate([2]) == simple_filter_.evaluate([2]) - remove(path_) + path.unlink() def test_load_complex_sfg(self, precedence_sfg_delays_and_constants): result = sfg_to_python(precedence_sfg_delays_and_constants) - path_ = self.get_path() + path = self.get_path() - assert not path.exists(path_) - with open(path_, "w") as file_obj: + assert not path.exists() + with path.open("w") as file_obj: file_obj.write(result) - assert path.exists(path_) + assert path.exists() - precedence_sfg_registers_and_constants_, _ = python_to_sfg(path_) + precedence_sfg_registers_and_constants_, _ = python_to_sfg(path) assert str(precedence_sfg_delays_and_constants) == str( precedence_sfg_registers_and_constants_ ) - remove(path_) + path.unlink() def test_load_invalid_path(self): - path_ = self.get_path(existing=False) + path = self.get_path(existing=False) with pytest.raises(FileNotFoundError): - python_to_sfg(path_) + python_to_sfg(path) class TestGetComponentsOfType: -- GitLab