~sschwarzer/ftputil

ftputil/find_problematic_code.py -rwxr-xr-x 3.8 KiB
77f2ca24Stefan Schwarzer Move item "Push to repository" a month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#! /usr/bin/env python3
# Copyright (C) 2008-2020, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# and ftputil contributors (see `doc/contributors.txt`)
# See the file LICENSE for licensing terms.

# pylint: disable=redefined-builtin

"""\
This script scans a directory tree for files which contain code which
may cause problems in ftputil %s and above. The script uses simple
heuristics, so it may miss occurrences of problematic usage or print
some harmless lines of your files.

Usage: %s start_dir

where 'start_dir' is the starting directory which will be scanned
recursively for potentially problematic code.
"""

import os
import re
import sys

import ftputil.version


__doc__ = __doc__ % (ftputil.version.__version__, os.path.basename(sys.argv[0]))


class InvalidFeature:
    """
    Store message, regex and locations of a single now invalid
    feature.
    """

    # pylint: disable=too-few-public-methods
    def __init__(self, message, regex):
        self.message = message
        if not isinstance(regex, re.compile("").__class__):
            regex = re.compile(regex)
        self.regex = regex
        # Map file name to a list of line numbers (starting at 1).
        self.locations = {}


HOST_REGEX = r"\b(h|host|ftp|ftphost|ftp_host)\b"

invalid_features = [
    InvalidFeature(
        "Definition of time shift has changed in ftputil 4.0.0",
        r"\.(time_shift\(\)|set_time_shift\()",
    ),
    InvalidFeature(
        "Behavior of `FTPHost.makedirs` has changed in ftputil 4.0.0", r"\.makedirs\("
    ),
]


def scan_file(file_name):
    """
    Scan a file with name `file_name` for code deprecated in
    ftputil usage and collect the offending data in the dictionary
    `features.locations`.
    """
    with open(file_name) as fobj:
        for index, line in enumerate(fobj, start=1):
            for feature in invalid_features:
                if feature.regex.search(line):
                    locations = feature.locations
                    locations.setdefault(file_name, [])
                    locations[file_name].append((index, line.rstrip()))


def print_results():
    """
    Print statistics of deprecated code after the directory has been
    scanned.
    """
    last_message = ""
    for feature in invalid_features:
        if feature.message != last_message:
            print()
            print(70 * "-")
            print(feature.message, "...")
            print()
            last_message = feature.message
        locations = feature.locations
        if not locations:
            print("   no problematic code found")
            continue
        for file_name in sorted(locations.keys()):
            print(file_name)
            for line_number, line in locations[file_name]:
                print("%5d: %s" % (line_number, line))
    print()
    print("===========================================")
    print("Please check your code also by other means!")
    print("===========================================")


def main(start_dir):
    """
    Scan a directory tree starting at `start_dir` and print uses
    of deprecated features, if any were found.
    """
    # Deliberately shadow global `start_dir`.
    # pylint: disable=redefined-outer-name
    for dir_path, _dirnames, file_names in os.walk(start_dir):
        for file_name in file_names:
            abs_name = os.path.abspath(os.path.join(dir_path, file_name))
            if file_name.endswith(".py"):
                scan_file(abs_name)
    print_results()


if __name__ == "__main__":
    if len(sys.argv) == 2:
        if sys.argv[1] in ("-h", "--help"):
            print(__doc__)
            sys.exit(0)
        start_dir = sys.argv[1]
        if not os.path.isdir(start_dir):
            print("Directory %s not found." % start_dir, file=sys.stderr)
            sys.exit()
    else:
        print("Usage: %s start_dir" % sys.argv[0], file=sys.stderr)
        sys.exit()
    main(start_dir)