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 <nbehrnd@yahoo.com>
This commit is contained in:
Norwid Behrnd 2026-03-02 12:42:30 +01:00
parent c7691fff23
commit 7ca34e04a0
No known key found for this signature in database
2 changed files with 29 additions and 2 deletions

View file

@ -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()

View file

@ -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),