source: find_invalid_code.py @ 1723:7d731aea5360

Last change on this file since 1723:7d731aea5360 was 1723:7d731aea5360, checked in by Stefan Schwarzer <sschwarzer@…>, 8 months ago
Remove positional argument specifiers for `format` In strings for `format` calls, remove the digits for positional arguments. For example, "{0}, {1}" becomes "{}, {}".
  • Property exe set to *
File size: 4.0 KB
Line 
1#! /usr/bin/env python
2# Copyright (C) 2008-2018, Stefan Schwarzer <sschwarzer@sschwarzer.net>
3# and ftputil contributors (see `doc/contributors.txt`)
4# See the file LICENSE for licensing terms.
5
6# pylint: disable=redefined-builtin
7
8"""\
9This script scans a directory tree for files which contain code which
10is deprecated or invalid in ftputil %s and above (and even much
11longer). The script uses simple heuristics, so it may miss occurrences
12of deprecated/invalid usage or print some inappropriate lines of your
13files.
14
15Usage: %s start_dir
16
17where 'start_dir' is the starting directory which will be scanned
18recursively for offending code.
19"""
20
21import os
22import re
23import sys
24
25import ftputil.version
26
27
28__doc__ = __doc__ % (ftputil.version.__version__,
29                     os.path.basename(sys.argv[0]))
30
31
32class InvalidFeature:
33    """
34    Store message, regex and locations of a single now invalid
35    feature.
36    """
37
38    # pylint: disable=too-few-public-methods
39    def __init__(self, message, regex):
40        self.message = message
41        if not isinstance(regex, re.compile("").__class__):
42            regex = re.compile(regex)
43        self.regex = regex
44        # Map file name to a list of line numbers (starting at 1).
45        self.locations = {}
46
47
48HOST_REGEX = r"\b(h|host|ftp|ftphost|ftp_host)\b"
49
50invalid_features = [
51  InvalidFeature("Possible use(s) of FTP exceptions via ftputil module",
52                 r"\bftputil\s*?\.\s*?[A-Za-z]+Error\b"),
53  InvalidFeature("Possible use(s) of ftp_error module",
54                 r"\bftp_error\b"),
55  InvalidFeature("Possible use(s) of ftp_stat module",
56                 r"\bftp_stat\b"),
57  InvalidFeature("Possible use(s) of FTPHost.file",
58                 r"{}\.file\(".format(HOST_REGEX))
59]
60
61
62def scan_file(file_name):
63    """
64    Scan a file with name `file_name` for code deprecated in
65    ftputil usage and collect the offending data in the dictionary
66    `features.locations`.
67    """
68    with open(file_name) as fobj:
69        for index, line in enumerate(fobj, start=1):
70            for feature in invalid_features:
71                if feature.regex.search(line):
72                    locations = feature.locations
73                    locations.setdefault(file_name, [])
74                    locations[file_name].append((index, line.rstrip()))
75
76
77def print_results():
78    """
79    Print statistics of deprecated code after the directory has been
80    scanned.
81    """
82    last_message = ""
83    for feature in invalid_features:
84        if feature.message != last_message:
85            print()
86            print(70 * "-")
87            print(feature.message, "...")
88            print()
89            last_message = feature.message
90        locations = feature.locations
91        if not locations:
92            print("   no invalid code found")
93            continue
94        for file_name in sorted(locations.keys()):
95            print(file_name)
96            for line_number, line in locations[file_name]:
97                print("%5d: %s" % (line_number, line))
98    print()
99    print("===========================================")
100    print("Please check your code also by other means!")
101    print("===========================================")
102
103
104def main(start_dir):
105    """
106    Scan a directory tree starting at `start_dir` and print uses
107    of deprecated features, if any were found.
108    """
109    # Deliberately shadow global `start_dir`.
110    # pylint: disable=redefined-outer-name
111    for dir_path, _dirnames, file_names in os.walk(start_dir):
112        for file_name in file_names:
113            abs_name = os.path.abspath(os.path.join(dir_path, file_name))
114            if file_name.endswith(".py"):
115                scan_file(abs_name)
116    print_results()
117
118
119if __name__ == '__main__':
120    if len(sys.argv) == 2:
121        if sys.argv[1] in ("-h", "--help"):
122            print(__doc__)
123            sys.exit(0)
124        start_dir = sys.argv[1]
125        if not os.path.isdir(start_dir):
126            print("Directory %s not found." % start_dir, file=sys.stderr)
127            sys.exit()
128    else:
129        print("Usage: %s start_dir" % sys.argv[0], file=sys.stderr)
130        sys.exit()
131    main(start_dir)
Note: See TracBrowser for help on using the repository browser.