oct
Loading...
Searching...
No Matches
fs_remove Namespace Reference

Functions

bool remove_tree_force (Path target)
bool remove_tree_or_warn (Path target, *, str label="")

Variables

tuple READ_ONLY_HINT

Detailed Description

Purpose
-------
One recursive-remove that survives a read-only directory, for every ``oct``
command that deletes a generated tree.

The defect this replaces: ``shutil.rmtree(path)`` raises
``PermissionError: [WinError 5] Access is denied`` when a directory carries the
Windows read-only attribute, because ``os.rmdir`` refuses it. Its
``ignore_errors=True`` form is no better -- it swallows the refusal and leaves
the tree standing while the caller reports success. On 2026-09-07 that let
``oct clean`` count 947 surviving directories as removed; on 2026-09-25 the
bare form ended ``oct docs --all`` in a traceback out of
``run_sphinx_build``'s pre-build clean.

The attribute is not exotic here (ENV-03): a Google Drive or OneDrive mirror
sets it on every directory it syncs, so an entire checkout under a mirror root
has it. Clearing the mirror is the real remedy and stays an operator action;
this module is what makes the tools survive until then.

Responsibilities
----------------
- :func:`remove_tree_force` -- remove a tree, clearing the read-only attribute
  on whatever refuses, and report whether it is really gone.
- :func:`remove_tree_or_warn` -- the same, plus the one standard warning, so
  six call sites do not word the same failure six ways.
- :data:`READ_ONLY_HINT` -- that wording, in one place.

Diagnostics
-----------
Domain: OCT-CORE
Levels:
    L1 -- a tree that is still standing after the retry
    L2 -- lifecycle (a tree removed)
    L3 -- semantic details (the path, and the retry firing)
    L4 -- deep tracing

Contracts
---------
- The verdict is the filesystem's, never the call's: the return value is a
  fresh existence check, so a swallowed error cannot read as success. This is
  the property ``ignore_errors=True`` lacks and the reason it is not used here.
- Failure is never raised. Every caller here is deleting generated output as
  one step of a longer pipeline, and an unremovable cache directory must not
  end the run with a traceback.
- ``False`` does NOT mean nothing was deleted. A recursive delete works
  bottom-up, so a refusal at the final ``rmdir`` has already taken the files
  and leaves the directory shell behind. A caller that plans to rebuild into
  the path should copy or build into what remains (``exist_ok`` /
  ``dirs_exist_ok``) rather than treat the target as still intact.
- The attribute is cleared only on a path that has already refused removal and
  only to delete it. Nothing is made writable and left behind.
- ``target`` that does not exist is success -- the postcondition ("it is not
  there") already holds.
- stdlib plus the ``_dbg`` shim only; imports nothing of ``oct``'s beyond
  diagnostics, so any module may call it.

Dependencies
------------
- oct.core.diagnostics (the ``_dbg`` shim)

Function Documentation

◆ remove_tree_force()

bool fs_remove.remove_tree_force ( Path target)
Remove *target* recursively and report whether it is really gone.

Clears the read-only attribute on whatever refuses and retries that one
path once. Returns the result of an existence check made afterwards, so the
answer is what is on disk rather than what the call believed.

Definition at line 86 of file fs_remove.py.

Here is the caller graph for this function:

◆ remove_tree_or_warn()

bool fs_remove.remove_tree_or_warn ( Path target,
* ,
str label = "" )
Remove *target*; on failure print one standard warning and return False.

*label* names the thing in the operator's terms ("Sphinx doctree cache"),
so the warning says what was not cleaned as well as where it is.

Definition at line 122 of file fs_remove.py.

Here is the call graph for this function:

Variable Documentation

◆ READ_ONLY_HINT

tuple fs_remove.READ_ONLY_HINT
Initial value:
1= (
2 "it or something inside it is read-only or locked. On Windows a Google "
3 "Drive / OneDrive mirror sets the read-only attribute on every synced "
4 "directory; clear it with: attrib -R /S /D <path>"
5)

Definition at line 79 of file fs_remove.py.