Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/pyflakes/api.py: 20%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2API for the command-line I{pyflakes} tool.
3"""
4import ast
5import os
6import platform
7import re
8import sys
10from pyflakes import checker, __version__
11from pyflakes import reporter as modReporter
13__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
15PYTHON_SHEBANG_REGEX = re.compile(br'^#!.*\bpython(3(\.\d+)?|w)?[dmu]?\s')
18def check(codeString, filename, reporter=None):
19 """
20 Check the Python source given by C{codeString} for flakes.
22 @param codeString: The Python source to check.
23 @type codeString: C{str}
25 @param filename: The name of the file the source came from, used to report
26 errors.
27 @type filename: C{str}
29 @param reporter: A L{Reporter} instance, where errors and warnings will be
30 reported.
32 @return: The number of warnings emitted.
33 @rtype: C{int}
34 """
35 if reporter is None:
36 reporter = modReporter._makeDefaultReporter()
37 # First, compile into an AST and handle syntax errors.
38 try:
39 tree = ast.parse(codeString, filename=filename)
40 except SyntaxError as e:
41 reporter.syntaxError(filename, e.args[0], e.lineno, e.offset, e.text)
42 return 1
43 except Exception:
44 reporter.unexpectedError(filename, 'problem decoding source')
45 return 1
46 # Okay, it's syntactically valid. Now check it.
47 w = checker.Checker(tree, filename=filename)
48 w.messages.sort(key=lambda m: m.lineno)
49 for warning in w.messages:
50 reporter.flake(warning)
51 return len(w.messages)
54def checkPath(filename, reporter=None):
55 """
56 Check the given path, printing out any warnings detected.
58 @param reporter: A L{Reporter} instance, where errors and warnings will be
59 reported.
61 @return: the number of warnings printed
62 """
63 if reporter is None:
64 reporter = modReporter._makeDefaultReporter()
65 try:
66 with open(filename, 'rb') as f:
67 codestr = f.read()
68 except OSError as e:
69 reporter.unexpectedError(filename, e.args[1])
70 return 1
71 return check(codestr, filename, reporter)
74def isPythonFile(filename):
75 """Return True if filename points to a Python file."""
76 if filename.endswith('.py'):
77 return True
79 # Avoid obvious Emacs backup files
80 if filename.endswith("~"):
81 return False
83 max_bytes = 128
85 try:
86 with open(filename, 'rb') as f:
87 text = f.read(max_bytes)
88 if not text:
89 return False
90 except OSError:
91 return False
93 return PYTHON_SHEBANG_REGEX.match(text)
96def iterSourceCode(paths):
97 """
98 Iterate over all Python source files in C{paths}.
100 @param paths: A list of paths. Directories will be recursed into and
101 any .py files found will be yielded. Any non-directories will be
102 yielded as-is.
103 """
104 for path in paths:
105 if os.path.isdir(path):
106 for dirpath, dirnames, filenames in os.walk(path):
107 for filename in filenames:
108 full_path = os.path.join(dirpath, filename)
109 if isPythonFile(full_path):
110 yield full_path
111 else:
112 yield path
115def checkRecursive(paths, reporter):
116 """
117 Recursively check all source files in C{paths}.
119 @param paths: A list of paths to Python source files and directories
120 containing Python source files.
121 @param reporter: A L{Reporter} where all of the warnings and errors
122 will be reported to.
123 @return: The number of warnings found.
124 """
125 warnings = 0
126 for sourcePath in iterSourceCode(paths):
127 warnings += checkPath(sourcePath, reporter)
128 return warnings
131def _exitOnSignal(sigName, message):
132 """Handles a signal with sys.exit.
134 Some of these signals (SIGPIPE, for example) don't exist or are invalid on
135 Windows. So, ignore errors that might arise.
136 """
137 import signal
139 try:
140 sigNumber = getattr(signal, sigName)
141 except AttributeError:
142 # the signal constants defined in the signal module are defined by
143 # whether the C library supports them or not. So, SIGPIPE might not
144 # even be defined.
145 return
147 def handler(sig, f):
148 sys.exit(message)
150 try:
151 signal.signal(sigNumber, handler)
152 except ValueError:
153 # It's also possible the signal is defined, but then it's invalid. In
154 # this case, signal.signal raises ValueError.
155 pass
158def _get_version():
159 """
160 Retrieve and format package version along with python version & OS used
161 """
162 return ('%s Python %s on %s' %
163 (__version__, platform.python_version(), platform.system()))
166def main(prog=None, args=None):
167 """Entry point for the script "pyflakes"."""
168 import argparse
170 # Handle "Keyboard Interrupt" and "Broken pipe" gracefully
171 _exitOnSignal('SIGINT', '... stopped')
172 _exitOnSignal('SIGPIPE', 1)
174 parser = argparse.ArgumentParser(prog=prog,
175 description='Check Python source files for errors')
176 parser.add_argument('-V', '--version', action='version', version=_get_version())
177 parser.add_argument('path', nargs='*',
178 help='Path(s) of Python file(s) to check. STDIN if not given.')
179 args = parser.parse_args(args=args).path
180 reporter = modReporter._makeDefaultReporter()
181 if args:
182 warnings = checkRecursive(args, reporter)
183 else:
184 warnings = check(sys.stdin.read(), '<stdin>', reporter)
185 raise SystemExit(warnings > 0)