#! /usr/bin/env python
# -*- coding: utf-8 -*-
# <https://code.activestate.com/recipes/580709-lines-of-code-loc/>
# Basic Lines-Of-Code counter in Python source files, reporting the
# number of blank, comment and source code lines and total number of
# lines in all Python files scanned.
# Usage example:
# % python locs.py -rec ~/Projects
# 8691 *.py files: 365038 blank (14.0%), 212100 comment (8.1%),
# 2030198 source (77.9%), 2607336 total lines
# (2.739 secs, 951872 lines/sec)
# % python3 locs.py -rec ~/Projects
# 8691 *.py files: 365037 blank (14.0%), 212100 comment (8.1%),
# 2030198 source (77.9%), 2607335 total lines
# (2.599 secs, 1003158 lines/sec)
# % python3 locs.py -h
# usage: locs.py [-help] [-recurse] [-verbose] <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.25'
class Loc(object):
'''Lines-Of-Code accumulator.
'''
blank = 0
comment = 0
files = 0
source = 0
ext = '.py'
_time0 = 0
_recurse = False # process dirs
_verbose = False # print details
def __init__(self, recurse=False, verbose=False):
if recurse:
self._recurse = recurse
if verbose:
self._verbose = verbose
self._time0 = time()
def __str__(self):
s = time() - self._time0
n = self.source + self.comment + self.blank
p = int(n / s) if n > s > 0 else '-'
t = ['%s *%s files:' % (self.files, self.ext),
self._bcst(self.blank, self.comment, self.source),
'(%.3f secs, %s lines/sec)' % (s, p)]
return ' '.join(t)
def _bcst(self, blank, comment, source):
t, n = [], blank + comment + source
for a, v in (('blank', blank),
('comment', comment),
('source', source)):
p = ' (%.1f%%)' % ((v * 100.0) / n,) if n > 0 else ''
t.append('%s %s%s' % (v, a, p))
t.append('%s total lines' % (n,))
return ', '.join(t)
def adir(self, name):
'''Process a directory.
'''
if self._recurse:
if self._verbose:
print(' dir %s: %s' % (name, '...'))
b, c, s = self.blank, self.comment, self.source
self.aglob(join(name, '*'))
b = self.blank - b
c = self.comment - c
s = self.source - s
t = name, self._bcst(b, c, s)
print(' dir %s: %s' % t)
else:
self.aglob(join(name, '*'))
def afile(self, name):
'''Process a file.
'''
if name.endswith(self.ext) and exists(name):
b = c = s = 0
with open(name, 'rb') as f:
for t in f.readlines():
t = t.lstrip()
if not t:
b += 1
elif t.startswith(b'#'): # Python 3+
c += 1
else:
s += 1
self.blank += b
self.comment += c
self.source += s
self.files += 1
if self._verbose:
t = self.files, name, self._bcst(b, c, s)
print('file %s %s: %s' % t)
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] [-recurse] [-verbose] <file_or_dir_name> ...' % (argv0,))
sys.exit(0)
elif '-recurse'.startswith(arg):
loc._recurse = True
elif '-verbose'.startswith(arg):
loc._verbose = True
elif arg != '--':
print('%s: invalid option: %r' % (argv0, arg))
sys.exit(1)
except KeyboardInterrupt:
print('')
print('%s' % (loc,))
Diff to Previous Revision
--- revision 2 2016-10-24 20:13:46
+++ revision 3 2016-10-25 17:53:01
@@ -1,24 +1,26 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
+# <https://code.activestate.com/recipes/580709-lines-of-code-loc/>
+
# 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.
+# number of blank, comment and source code 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)
+# 8691 *.py files: 365038 blank (14.0%), 212100 comment (8.1%),
+# 2030198 source (77.9%), 2607336 total lines
+# (2.739 secs, 951872 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)
+# 8691 *.py files: 365037 blank (14.0%), 212100 comment (8.1%),
+# 2030198 source (77.9%), 2607335 total lines
+# (2.599 secs, 1003158 lines/sec)
# % python3 locs.py -h
-# usage: locs.py [-help] [-log] [-recurse] <file_or_dir_name> ...
+# usage: locs.py [-help] [-recurse] [-verbose] <file_or_dir_name> ...
# Tested with 64-bit Python 2.7.10 and 3.5.1 on MacOS 10.11.6 only.
@@ -27,7 +29,7 @@
from time import time
__all__ = ('Loc',)
-__version__ = '16.10.24'
+__version__ = '16.10.25'
class Loc(object):
@@ -37,56 +39,77 @@
comment = 0
files = 0
source = 0
+ ext = '.py'
- ext = '.py'
- log = False # print process
- rec = False # recursively process dirs
- _t0 = 0
+ _time0 = 0
- def __init__(self, log=False, rec=False):
- if log:
- self.log = log
- if rec:
- self.rec = rec
- self._t0 = time()
+ _recurse = False # process dirs
+ _verbose = False # print details
+
+ def __init__(self, recurse=False, verbose=False):
+ if recurse:
+ self._recurse = recurse
+ if verbose:
+ self._verbose = verbose
+ self._time0 = time()
def __str__(self):
+ s = time() - self._time0
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))
+ p = int(n / s) if n > s > 0 else '-'
+ t = ['%s *%s files:' % (self.files, self.ext),
+ self._bcst(self.blank, self.comment, self.source),
+ '(%.3f secs, %s lines/sec)' % (s, p)]
return ' '.join(t)
+
+ def _bcst(self, blank, comment, source):
+ t, n = [], blank + comment + source
+ for a, v in (('blank', blank),
+ ('comment', comment),
+ ('source', source)):
+ p = ' (%.1f%%)' % ((v * 100.0) / n,) if n > 0 else ''
+ t.append('%s %s%s' % (v, a, p))
+ t.append('%s total lines' % (n,))
+ return ', '.join(t)
def adir(self, name):
'''Process a directory.
'''
- if self.rec:
- self.aglob(join(name, '*'))
+ if self._recurse:
+ if self._verbose:
+ print(' dir %s: %s' % (name, '...'))
+ b, c, s = self.blank, self.comment, self.source
+ self.aglob(join(name, '*'))
+ b = self.blank - b
+ c = self.comment - c
+ s = self.source - s
+ t = name, self._bcst(b, c, s)
+ print(' dir %s: %s' % t)
+ else:
+ 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))
+ b = c = s = 0
with open(name, 'rb') as f:
for t in f.readlines():
t = t.lstrip()
if not t:
- self.blank += 1
+ b += 1
elif t.startswith(b'#'): # Python 3+
- self.comment += 1
+ c += 1
else:
- self.source += 1
+ s += 1
+
+ self.blank += b
+ self.comment += c
+ self.source += s
+ self.files += 1
+ if self._verbose:
+ t = self.files, name, self._bcst(b, c, s)
+ print('file %s %s: %s' % t)
def aglob(self, wild):
'''Process a possible wildcard.
@@ -111,12 +134,12 @@
loc.aglob(arg)
elif '-help'.startswith(arg):
- print('usage: %s [-help] [-log] [-recurse] <file_or_dir_name> ...' % (argv0,))
+ print('usage: %s [-help] [-recurse] [-verbose] <file_or_dir_name> ...' % (argv0,))
sys.exit(0)
- elif '-log'.startswith(arg):
- loc.log = True
elif '-recurse'.startswith(arg):
- loc.rec = True
+ loc._recurse = True
+ elif '-verbose'.startswith(arg):
+ loc._verbose = True
elif arg != '--':
print('%s: invalid option: %r' % (argv0, arg))
sys.exit(1)
@@ -124,4 +147,4 @@
except KeyboardInterrupt:
print('')
- print('%s: %s' % (argv0, loc))
+ print('%s' % (loc,))