ยปCore Development>Code coverage>Tools/scripts/patchcheck.py

Python code coverage for Tools/scripts/patchcheck.py

#countcontent
1n/a#!/usr/bin/env python3
2n/aimport re
3n/aimport sys
4n/aimport shutil
5n/aimport os.path
6n/aimport subprocess
7n/aimport sysconfig
8n/a
9n/aimport reindent
10n/aimport untabify
11n/a
12n/a
13n/aSRCDIR = sysconfig.get_config_var('srcdir')
14n/a
15n/a
16n/adef n_files_str(count):
17n/a """Return 'N file(s)' with the proper plurality on 'file'."""
18n/a return "{} file{}".format(count, "s" if count != 1 else "")
19n/a
20n/a
21n/adef status(message, modal=False, info=None):
22n/a """Decorator to output status info to stdout."""
23n/a def decorated_fxn(fxn):
24n/a def call_fxn(*args, **kwargs):
25n/a sys.stdout.write(message + ' ... ')
26n/a sys.stdout.flush()
27n/a result = fxn(*args, **kwargs)
28n/a if not modal and not info:
29n/a print("done")
30n/a elif info:
31n/a print(info(result))
32n/a else:
33n/a print("yes" if result else "NO")
34n/a return result
35n/a return call_fxn
36n/a return decorated_fxn
37n/a
38n/a
39n/adef mq_patches_applied():
40n/a """Check if there are any applied MQ patches."""
41n/a cmd = 'hg qapplied'
42n/a with subprocess.Popen(cmd.split(),
43n/a stdout=subprocess.PIPE,
44n/a stderr=subprocess.PIPE) as st:
45n/a bstdout, _ = st.communicate()
46n/a return st.returncode == 0 and bstdout
47n/a
48n/a
49n/a@status("Getting the list of files that have been added/changed",
50n/a info=lambda x: n_files_str(len(x)))
51n/adef changed_files():
52n/a """Get the list of changed or added files from Mercurial or git."""
53n/a if os.path.isdir(os.path.join(SRCDIR, '.hg')):
54n/a cmd = 'hg status --added --modified --no-status'
55n/a if mq_patches_applied():
56n/a cmd += ' --rev qparent'
57n/a with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
58n/a return [x.decode().rstrip() for x in st.stdout]
59n/a elif os.path.isdir(os.path.join(SRCDIR, '.git')):
60n/a cmd = 'git status --porcelain'
61n/a filenames = []
62n/a with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
63n/a for line in st.stdout:
64n/a line = line.decode().rstrip()
65n/a status = set(line[:2])
66n/a # modified, added or unmerged files
67n/a if not status.intersection('MAU'):
68n/a continue
69n/a filename = line[3:]
70n/a if ' -> ' in filename:
71n/a # file is renamed
72n/a filename = filename.split(' -> ', 2)[1].strip()
73n/a filenames.append(filename)
74n/a return filenames
75n/a else:
76n/a sys.exit('need a Mercurial or git checkout to get modified files')
77n/a
78n/a
79n/adef report_modified_files(file_paths):
80n/a count = len(file_paths)
81n/a if count == 0:
82n/a return n_files_str(count)
83n/a else:
84n/a lines = ["{}:".format(n_files_str(count))]
85n/a for path in file_paths:
86n/a lines.append(" {}".format(path))
87n/a return "\n".join(lines)
88n/a
89n/a
90n/a@status("Fixing whitespace", info=report_modified_files)
91n/adef normalize_whitespace(file_paths):
92n/a """Make sure that the whitespace for .py files have been normalized."""
93n/a reindent.makebackup = False # No need to create backups.
94n/a fixed = [path for path in file_paths if path.endswith('.py') and
95n/a reindent.check(os.path.join(SRCDIR, path))]
96n/a return fixed
97n/a
98n/a
99n/a@status("Fixing C file whitespace", info=report_modified_files)
100n/adef normalize_c_whitespace(file_paths):
101n/a """Report if any C files """
102n/a fixed = []
103n/a for path in file_paths:
104n/a abspath = os.path.join(SRCDIR, path)
105n/a with open(abspath, 'r') as f:
106n/a if '\t' not in f.read():
107n/a continue
108n/a untabify.process(abspath, 8, verbose=False)
109n/a fixed.append(path)
110n/a return fixed
111n/a
112n/a
113n/aws_re = re.compile(br'\s+(\r?\n)$')
114n/a
115n/a@status("Fixing docs whitespace", info=report_modified_files)
116n/adef normalize_docs_whitespace(file_paths):
117n/a fixed = []
118n/a for path in file_paths:
119n/a abspath = os.path.join(SRCDIR, path)
120n/a try:
121n/a with open(abspath, 'rb') as f:
122n/a lines = f.readlines()
123n/a new_lines = [ws_re.sub(br'\1', line) for line in lines]
124n/a if new_lines != lines:
125n/a shutil.copyfile(abspath, abspath + '.bak')
126n/a with open(abspath, 'wb') as f:
127n/a f.writelines(new_lines)
128n/a fixed.append(path)
129n/a except Exception as err:
130n/a print('Cannot fix %s: %s' % (path, err))
131n/a return fixed
132n/a
133n/a
134n/a@status("Docs modified", modal=True)
135n/adef docs_modified(file_paths):
136n/a """Report if any file in the Doc directory has been changed."""
137n/a return bool(file_paths)
138n/a
139n/a
140n/a@status("Misc/ACKS updated", modal=True)
141n/adef credit_given(file_paths):
142n/a """Check if Misc/ACKS has been changed."""
143n/a return os.path.join('Misc', 'ACKS') in file_paths
144n/a
145n/a
146n/a@status("Misc/NEWS updated", modal=True)
147n/adef reported_news(file_paths):
148n/a """Check if Misc/NEWS has been changed."""
149n/a return os.path.join('Misc', 'NEWS') in file_paths
150n/a
151n/a@status("configure regenerated", modal=True, info=str)
152n/adef regenerated_configure(file_paths):
153n/a """Check if configure has been regenerated."""
154n/a if 'configure.ac' in file_paths:
155n/a return "yes" if 'configure' in file_paths else "no"
156n/a else:
157n/a return "not needed"
158n/a
159n/a@status("pyconfig.h.in regenerated", modal=True, info=str)
160n/adef regenerated_pyconfig_h_in(file_paths):
161n/a """Check if pyconfig.h.in has been regenerated."""
162n/a if 'configure.ac' in file_paths:
163n/a return "yes" if 'pyconfig.h.in' in file_paths else "no"
164n/a else:
165n/a return "not needed"
166n/a
167n/adef main():
168n/a file_paths = changed_files()
169n/a python_files = [fn for fn in file_paths if fn.endswith('.py')]
170n/a c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
171n/a doc_files = [fn for fn in file_paths if fn.startswith('Doc') and
172n/a fn.endswith(('.rst', '.inc'))]
173n/a misc_files = {os.path.join('Misc', 'ACKS'), os.path.join('Misc', 'NEWS')}\
174n/a & set(file_paths)
175n/a # PEP 8 whitespace rules enforcement.
176n/a normalize_whitespace(python_files)
177n/a # C rules enforcement.
178n/a normalize_c_whitespace(c_files)
179n/a # Doc whitespace enforcement.
180n/a normalize_docs_whitespace(doc_files)
181n/a # Docs updated.
182n/a docs_modified(doc_files)
183n/a # Misc/ACKS changed.
184n/a credit_given(misc_files)
185n/a # Misc/NEWS changed.
186n/a reported_news(misc_files)
187n/a # Regenerated configure, if necessary.
188n/a regenerated_configure(file_paths)
189n/a # Regenerated pyconfig.h.in, if necessary.
190n/a regenerated_pyconfig_h_in(file_paths)
191n/a
192n/a # Test suite run and passed.
193n/a if python_files or c_files:
194n/a end = " and check for refleaks?" if c_files else "?"
195n/a print()
196n/a print("Did you run the test suite" + end)
197n/a
198n/a
199n/aif __name__ == '__main__':
200n/a main()