Welcome, guest | Sign In | My Account | Store | Cart
#! /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,))

History