#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Basic Lines-Of-Code counter in Python source files, reporting the
# number of source code lines, comment lines, blank lines and total
# number of lines in all Python files scanned.
# Usage example:
# % python locs.py -rec ~/Projects
# locs.py: 8689 *.py files: 2030008 source (77.9%), 212064 comment (8.1%),
# 364984 blank (14.0%), 2607056 total lines
# (3.393 secs, 768473 lines/sec)
# % python3 locs.py -rec ~/Projects
# locs.py: 8689 *.py files: 2030008 source (77.9%), 212064 comment (8.1%),
# 364984 blank (14.0%), 2607056 total lines
# (3.029 secs, 860799 lines/sec)
# % python3 locs.py -h
# usage: locs.py [-help] [-log] [-recurse] <file_or_dir_name> ...
# Tested with 64-bit Python 2.7.10 and 3.5.1 on MacOS 10.11.6 only.
from glob import iglob
from os.path import basename, exists, isdir, join
from time import time
__all__ = ('Loc',)
__version__ = '16.10.24'
class Loc(object):
'''Lines-Of-Code accumulator.
'''
blank = 0
comment = 0
files = 0
source = 0
ext = '.py'
log = False # print process
rec = False # recursively process dirs
_t0 = 0
def __init__(self, log=False, rec=False):
if log:
self.log = log
if rec:
self.rec = rec
self._t0 = time()
def __str__(self):
n = self.source + self.comment + self.blank
s = time() - self._t0
t = ['%s *%s files:' % (self.files, self.ext)]
for a in ('source', 'comment', 'blank'):
v = getattr(self, a)
if n > 0:
p = ' (%.1f%%)' % ((v * 100.0) / n,)
else:
p = ''
t.append('%s %s%s,' % (v, a, p))
p = int(n / s)
t.append('%s total lines (%.3f secs, %s lines/sec)' % (n, s, p))
return ' '.join(t)
def adir(self, name):
'''Process a directory.
'''
if self.rec:
self.aglob(join(name, '*'))
def afile(self, name):
'''Process a file.
'''
if name.endswith(self.ext) and exists(name):
self.files += 1
if self.log:
print('file %s: %s' % (self.files, name))
with open(name, 'rb') as f:
for t in f.readlines():
t = t.lstrip()
if not t:
self.blank += 1
elif t.startswith(b'#'): # Python 3+
self.comment += 1
else:
self.source += 1
def aglob(self, wild):
'''Process a possible wildcard.
'''
for t in iglob(wild):
if isdir(t):
self.adir(t)
else:
self.afile(t)
if __name__ == '__main__':
import sys
argv0 = basename(sys.argv[0])
loc = Loc()
try:
for arg in sys.argv[1:]:
if not arg.startswith('-'):
loc.aglob(arg)
elif '-help'.startswith(arg):
print('usage: %s [-help] [-log] [-recurse] <file_or_dir_name> ...' % (argv0,))
sys.exit(0)
elif '-log'.startswith(arg):
loc.log = True
elif '-recurse'.startswith(arg):
loc.rec = True
elif arg != '--':
print('%s: invalid option: %r' % (argv0, arg))
sys.exit(1)
except KeyboardInterrupt:
print('')
print('%s: %s' % (argv0, loc))
Diff to Previous Revision
--- revision 1 2016-10-24 20:08:43
+++ revision 2 2016-10-24 20:13:46
@@ -80,13 +80,13 @@
print('file %s: %s' % (self.files, name))
with open(name, 'rb') as f:
for t in f.readlines():
- t = t.strip()
- if t.startswith(b'#'): # Python 3+
+ t = t.lstrip()
+ if not t:
+ self.blank += 1
+ elif t.startswith(b'#'): # Python 3+
self.comment += 1
- elif t:
+ else:
self.source += 1
- else:
- self.blank += 1
def aglob(self, wild):
'''Process a possible wildcard.