#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Line Counter
~~~~~~~~~~~~
Count the lines in given files.
Multiple patterns with shell-style wildcards (``*`` for
everything, ``?`` for any single character) are accepted.
Example usage and output::
$> python linecounter.py /some/path *.php *.html *.css
*.css: 982 lines
*.py: 4.739 lines
*.xhtml: 2.218 lines
---------------------
total: 7.939 lines
It returns the total for each pattern and an overall total.
Be aware that files will be included multiple times if you
specify overlapping patterns and so the result might not be
what you expected.
Python 2.5 is required.
:Copyright: 2005-2007 Jochen Kupperschmidt
:Date: 12-Jul-2007
:License: MIT
"""
from __future__ import with_statement
from glob import iglob
import locale
locale.setlocale(locale.LC_ALL, '')
from optparse import OptionParser
import os
def count_lines(filename):
"""Count lines in file."""
with open(filename, 'rb') as f:
return sum(1 for line in f)
def walk(top):
"""Walk file system tree and return directory names."""
yield top
for name in os.listdir(top):
name = os.path.join(top, name)
if os.path.isdir(name) and not os.path.islink(name):
for dir in walk(name):
yield dir
def match_filenames(path, patterns, callback):
"""Find files matching the pattern and count their lines."""
for dir in walk(path):
for pattern in patterns:
for filename in iglob(os.path.join(dir, pattern)):
line_count = count_lines(filename)
callback(filename, line_count)
yield pattern, line_count
def process_files(path, patterns, callback):
"""Collect line count statistics."""
stats = dict.fromkeys(patterns, 0)
for pattern, line_count in match_filenames(
path, patterns, callback):
stats[pattern] += line_count
return stats
def format_thousands(number):
"""Format number with thousands separated."""
return locale.format('%d', number, True)
def display_results(stats):
total = format_thousands(sum(stats.values()))
key_width = max(len(k) for k in stats.keys() + ['total'])
value_width = len(total)
format = '%%-%ds %%%ds lines' % (key_width + 1, value_width)
print
for key in sorted(stats.iterkeys()):
print format % (key + ':', format_thousands(stats[key]))
total_line = format % ('total:', total)
print '-' * len(total_line)
print total_line
if __name__ == '__main__':
parser = OptionParser(
usage='%prog [options] <path> [patterns]')
parser.add_option('-a', '--absolute', dest='absolute',
action='store_true',
help='show absolute paths in details (overrides `-r`)')
parser.add_option('-d', '--details', dest='details',
action='store_true',
help='show details for each file')
parser.add_option('-r', '--relative', dest='relative',
action='store_true',
help='show relative paths in details')
opts, args = parser.parse_args()
if not args:
parser.print_help()
parser.exit()
path, patterns = args[0], args[1:]
def callback(filename, line_count):
if opts.details:
path_len = len(path)
if opts.absolute:
filename = os.path.abspath(filename)
elif opts.relative:
filename = '.' + filename[path_len:]
print '%5d %s' % (line_count, filename)
stats = process_files(path, patterns, callback)
display_results(stats)