From 7ca34e04a0fc7927580f1cb168231e182b160056 Mon Sep 17 00:00:00 2001 From: Norwid Behrnd Date: Mon, 2 Mar 2026 12:42:30 +0100 Subject: [PATCH] fix(__init__.py): edit tree release in Windows Previously, tests about the deletion of tagtrees in GitHub's Windows osrunner failed, while the same tests in the runners of Ubuntu an MacOS passed. This likely is due how these files are "released" for deletion with greter ease (Linux/MacOS), or not (Windows). This is addressed by function `force_rmtree` added to `__init__.py`, result of a discussion with Claude AI/Sonnet 4.6. Simultaneously, this commit corrects the addition of the file extension `.lnk` in one of the tests. When submitted to the check by `ci_pytest.yml`, now each unit test compiled in `unit_tests.py` passes with Python 3.14 and either osrunner of Ubuntu 24.04.3, Microsoft Windows Server 2025 / 10.0.26100, and macOS 15.7.4 GitHub currently provides as ubuntu-latest, windows-latest, macos-latest. Signed-off-by: Norwid Behrnd --- filetags/__init__.py | 27 +++++++++++++++++++++++++++ tests/unit_tests.py | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/filetags/__init__.py b/filetags/__init__.py index ceec5b9..ab79464 100755 --- a/filetags/__init__.py +++ b/filetags/__init__.py @@ -53,6 +53,9 @@ try: except ModuleNotFoundError: have_tkinter = False +import stat +import tempfile + safe_import('operator') # for sorting dicts safe_import('difflib') # for good enough matching words safe_import('readline') # for raw_input() reading from stdin @@ -2521,6 +2524,7 @@ def assert_empty_tagfilter_directory(directory): if not options.dryrun: safe_import('shutil') # for removing directories with shutil.rmtree() shutil.rmtree(directory) + force_rmtree(directory) logging.debug('re-creating tagfilter directory "%s" ...' % str(directory)) os.makedirs(directory) @@ -3278,6 +3282,29 @@ def main(): successful_exit() +def force_rmtree(path): + """Provide a rmtree compatible both for Linux/MacOS and Windows. + + Previous definitions worked well for Linux/MacOS and their tests + by `unit_tests.py` launched by `pytest` passed with GitHub's + osrunners. However, the very same tests constantly failed with + the osrunner of Windows. After discussion, this function was + provided by Claude AI/Sonnet 4.6.""" + import stat, tempfile + def _remove_readonly(func, fpath, _exc): + os.chmod(fpath, stat.S_IWRITE) + func(fpath) + # Windows locks the CWD; move away before attempting deletion + try: + cwd = os.getcwd() + if os.path.abspath(cwd).startswith(os.path.abspath(path)): + os.chdir(tempfile.gettempdir()) + except Exception: + pass + if os.path.isdir(path): + rmtree(path, onerror=_remove_readonly) + + if __name__ == "__main__": try: main() diff --git a/tests/unit_tests.py b/tests/unit_tests.py index 99b210f..58c6ea2 100755 --- a/tests/unit_tests.py +++ b/tests/unit_tests.py @@ -417,8 +417,8 @@ class TestLocateAndParseControlledVocabulary(unittest.TestCase): # Note: cwd = subdir3 # Let's create all missing files in all dirs: - filetags.create_link(self.subdir2_file, self.subdir1_file) # create link - filetags.create_link(self.subdir2b_file, self.tempdir_file) # create link + filetags.create_link(self.subdir2_file, self.subdir1_file + ".lnk") # create link + filetags.create_link(self.subdir2b_file, self.tempdir_file + ".lnk") # create link # prio 1 = .filetag file in startfile directory self.assertEqual(filetags.locate_and_parse_controlled_vocabulary(self.subdir1_test_file),