pyclean.erase   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 90
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 45
dl 0
loc 90
rs 10
c 0
b 0
f 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A confirm() 0 8 2
A remove_freeform_targets() 0 24 2
C delete_filesystem_objects() 0 39 10
1
# SPDX-FileCopyrightText: 2020 Peter Bittner <[email protected]>
2
#
3
# SPDX-License-Identifier: GPL-3.0-or-later
4
5
"""Freeform target deletion with interactive prompt."""
6
7
import logging
8
from pathlib import Path
9
10
from .runner import Runner
11
12
log = logging.getLogger(__name__)
13
14
15
def confirm(message):
16
    """An interactive confirmation prompt."""
17
    try:
18
        answer = input('%s? ' % message)
19
        return answer.strip().lower() in ['y', 'yes']
20
    except KeyboardInterrupt:
21
        msg = 'Aborted by user.'
22
        raise SystemExit(msg)
23
24
25
def delete_filesystem_objects(
26
    directory: Path,
27
    path_glob: str,
28
    prompt=False,
29
    dry_run=False,
30
):
31
    """
32
    Identifies all pathnames matching a specific glob pattern, and attempts
33
    to delete them in the proper order, optionally asking for confirmation.
34
35
    Implementation Note: We sort the file system objects in *reverse order*
36
    and first delete *all files* before removing directories. This way we
37
    make sure that the directories that are deepest down in the hierarchy
38
    are empty (for both files & directories) when we attempt to remove them.
39
    """
40
    all_names = sorted(directory.glob(path_glob), reverse=True)
41
    dirs = (name for name in all_names if name.is_dir() and not name.is_symlink())
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable name does not seem to be defined.
Loading history...
42
    files = (name for name in all_names if not name.is_dir() or name.is_symlink())
43
44
    for file_object in files:
45
        file_type = 'symlink' if file_object.is_symlink() else 'file'
46
        if (
47
            not dry_run
48
            and prompt
49
            and not confirm('Delete %s %s' % (file_type, file_object))
50
        ):
51
            Runner.unlink_failed += 1
52
            continue
53
        Runner.unlink(file_object)
54
55
    for dir_object in dirs:
56
        if (
57
            not dry_run
58
            and prompt
59
            and not confirm('Remove empty directory %s' % dir_object)
60
        ):
61
            Runner.rmdir_failed += 1
62
            continue
63
        Runner.rmdir(dir_object)
64
65
66
def remove_freeform_targets(
67
    directory: Path,
68
    glob_patterns: list[str],
69
    yes,
70
    dry_run=False,
71
):
72
    """
73
    Remove free-form targets using globbing.
74
75
    This is **potentially dangerous** since users can delete everything
76
    anywhere in their file system, including the entire project they're
77
    working on. For this reason, the implementation imposes the following
78
    (user experience-related) restrictions:
79
80
    - Deleting (directories) is not recursive, directory contents must be
81
      explicitly specified using globbing (e.g. ``dirname/**/*``).
82
    - The user is responsible for the deletion order, so that a directory
83
      is empty when it is attempted to be deleted.
84
    - A confirmation prompt for the deletion of every single file system
85
      object is shown (unless the ``--yes`` option is used, in addition).
86
    """
87
    for path_glob in glob_patterns:
88
        log.debug('Erase file system objects matching: %s', path_glob)
89
        delete_filesystem_objects(directory, path_glob, prompt=not yes, dry_run=dry_run)
90