#! /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.strip() if t.startswith(b'#'): # Python 3+ self.comment += 1 elif t: self.source += 1 else: self.blank += 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))