Skip to content

Commit ab9c836

Browse files
committed
replace countlines.py with more advanced version from mcpyrate
1 parent 4c488b1 commit ab9c836

File tree

1 file changed

+48
-35
lines changed

1 file changed

+48
-35
lines changed

countlines.py

+48-35
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,73 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
"""Estimate project size in lines of code.
3+
"""Estimate project size in lines of code."""
44

5-
Ignores blank lines, docstrings, and whole-line comments."""
5+
# TODO: add sorting options: name, code count, SLOC count, code ratio.
66

77
import os
88
import re
99
from operator import itemgetter
1010

1111
def listpy(path):
12-
return list(sorted(fn for fn in os.listdir(path) if fn.endswith(".py")))
12+
return list(sorted(filename for filename in os.listdir(path) if filename.endswith(".py")))
1313

14-
def loc(code, blanks, docstrings, comments): # blanks et al.: include this item?
14+
def count_sloc(code, *, blanks, docstrings, comments):
15+
"""blanks et al.: include this item?"""
1516
if not docstrings:
1617
# TODO: make sure it's a docstring (and not some other """...""" string)
1718
code = re.sub(r'""".*?"""', r'', code, flags=(re.MULTILINE + re.DOTALL))
19+
code = re.sub(r"'''.*?'''", r'', code, flags=(re.MULTILINE + re.DOTALL))
1820
lines = code.split("\n")
1921
if not blanks:
2022
lines = [line for line in lines if line.strip()]
2123
if not comments:
22-
# TODO: removes only whole-line comments.
23-
lines = [line for line in lines if not line.strip().startswith("#")]
24+
lines = [line for line in lines if not line.strip().startswith("#")] # ignore whole-line comments
2425
return len(lines)
2526

26-
def analyze(items, blanks=False, docstrings=False, comments=False):
27-
grandtotal = 0
28-
for name, p in items:
29-
path = os.path.join(*p)
30-
files = listpy(path)
31-
ns = []
32-
for fn in files:
33-
with open(os.path.join(path, fn), "rt", encoding="utf-8") as f:
27+
def report(paths):
28+
print(f"Code size for {os.getcwd()}")
29+
def format_name(s, width=25):
30+
return s.ljust(width)
31+
def format_number(n, width=5):
32+
return str(n).rjust(width)
33+
def format_path(s): # ./subdir/something
34+
def label(s):
35+
if s == ".":
36+
return "top level"
37+
return s[2:]
38+
return format_name(label(s))
39+
codes_grandtotal = 0
40+
slocs_grandtotal = 0
41+
for path in paths:
42+
filenames = listpy(path)
43+
results = []
44+
for filename in filenames:
45+
with open(os.path.join(path, filename), "rt", encoding="utf-8") as f:
3446
content = f.read()
35-
ns.append(loc(content, blanks, docstrings, comments))
36-
# report
37-
print(f"{name}:")
38-
for fn, n in sorted(zip(files, ns), key=itemgetter(1)):
39-
print(f" {fn} {n}")
40-
grouptotal = sum(ns)
41-
print(f" total for {name} {grouptotal}")
42-
grandtotal += grouptotal
43-
print(f"grand total {grandtotal}")
47+
code = count_sloc(content, blanks=False, docstrings=False, comments=False)
48+
sloc = count_sloc(content, blanks=True, docstrings=True, comments=True)
49+
results.append((code, sloc))
50+
51+
if results:
52+
codes, slocs = zip(*results)
53+
codes = sum(codes)
54+
slocs = sum(slocs)
55+
print(f"\n {format_path(path)} {format_number(codes)} / {format_number(slocs)} {int(codes / slocs * 100):d}% code")
56+
for filename, (code, sloc) in sorted(zip(filenames, results), key=itemgetter(1)):
57+
print(f" {format_name(filename)} {format_number(code)} / {format_number(sloc)} {int(code / sloc * 100):d}% code")
58+
codes_grandtotal += codes
59+
slocs_grandtotal += slocs
60+
print(f"\n{format_name('Total')} {format_number(codes_grandtotal)} / {format_number(slocs_grandtotal)} {int(codes_grandtotal / slocs_grandtotal * 100):d}% code")
4461

4562
def main():
46-
items = (("top level", ["."]),
47-
("regular code", ["unpythonic"]),
48-
("regular code tests", ["unpythonic", "tests"]),
49-
("testing framework (not counting macros)", ["unpythonic", "test"]),
50-
("REPL/networking code", ["unpythonic", "net"]),
51-
("REPL/networking tests", ["unpythonic", "net", "tests"]),
52-
("macros", ["unpythonic", "syntax"]),
53-
("macro tests", ["unpythonic", "syntax", "tests"]))
54-
print("Raw (with blanks, docstrings and comments)")
55-
analyze(items, blanks=True, docstrings=True, comments=True)
56-
print("\nFiltered (non-blank code lines only)")
57-
analyze(items)
63+
blacklist = [".git", "build", "dist", "__pycache__", "00_stuff"]
64+
paths = []
65+
for root, dirs, files in os.walk("."):
66+
paths.append(root)
67+
for x in blacklist:
68+
if x in dirs:
69+
dirs.remove(x)
70+
report(sorted(paths))
5871

5972
if __name__ == '__main__':
6073
main()

0 commit comments

Comments
 (0)