Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

# -*- coding: UTF-8 -*- 

# Copyright 2012-2015 Luc Saffre 

# License: BSD (see file COPYING for details) 

 

""" 

Defines some utilities to inspect the running Python code. 

 

""" 

from builtins import str 

from builtins import object 

 

import logging 

logger = logging.getLogger(__name__) 

 

import os 

import sys 

import time 

import fnmatch 

 

 

def codefiles(pattern='*'): 

    """ 

    Yield a list of the source files corresponding to the currently 

    imported modules that match the given pattern 

    """ 

    #~ exp = re.compile(pattern, flags) 

 

    for name, mod in list(sys.modules.items()): 

        #~ if name == 'lino.extjs' and pattern == '*': 

            #~ logger.info("20130801 %r -> %r", name,mod.__file__) 

        if fnmatch.fnmatch(name, pattern): 

        #~ if exp.match(name): 

            filename = getattr(mod, "__file__", None) 

            if filename is not None: 

                if filename.endswith(".pyc") or filename.endswith(".pyo"): 

                    filename = filename[:-1] 

                if filename.endswith("$py.class"): 

                    filename = filename[:-9] + ".py" 

                # File might be in an egg, so there's no source available 

                if os.path.exists(filename): 

                    yield name, filename 

 

 

def codetime(*args, **kw): 

    """Return the modification time of the youngest source code in memory. 

 

    Used by :mod:`lino.modlib.extjs` to avoid generating .js files if 

    not necessary. 

 

    Inspired by the code_changed() function in `django.utils.autoreload`. 

 

    """ 

    code_mtime = None 

    pivot = None 

    for name, filename in codefiles(*args, **kw): 

        stat = os.stat(filename) 

        mtime = stat.st_mtime 

        if code_mtime is None or code_mtime < mtime: 

            # print 20130204, filename, time.ctime(mtime) 

            code_mtime = mtime 

            pivot = filename 

    # print '20130204 codetime:', time.ctime(code_mtime), pivot 

    return code_mtime 

 

 

def is_start_of_docstring(line): 

    for delim in '"""', "'''": 

        if line.startswith(delim) or line.startswith('u' + delim) or line.startswith('r' + delim) or line.startswith('ru' + delim): 

            return delim 

 

 

class SourceFile(object): 

 

    """ 

    Counts the number of code lines in a given Python source file. 

    """ 

 

    def __init__(self, modulename, filename): 

        self.modulename = modulename 

        self.filename = filename 

        self.analyze() 

 

    def analyze(self): 

        self.count_code, self.count_total, self.count_blank, self.count_doc = 0, 0, 0, 0 

        self.count_comment = 0 

        #~ count_code, count_total, count_blank, count_doc = 0, 0, 0, 0 

        skip_until = None 

        for line in open(self.filename).readlines(): 

            self.count_total += 1 

            line = line.strip() 

            if not line: 

                self.count_blank += 1 

            else: 

                if line.startswith('#'): 

                    self.count_comment += 1 

                    continue 

                if skip_until is None: 

                    skip_until = is_start_of_docstring(line) 

                    if skip_until is not None: 

                        self.count_doc += 1 

                        #~ skip_until = '"""' 

                        continue 

                    #~ if line.startswith('"""') or line.startswith('u"""'): 

                        #~ count_doc += 1 

                        #~ skip_until = '"""' 

                        #~ continue 

                    #~ if line.startswith("'''") or line.startswith("u'''"): 

                        #~ count_doc += 1 

                        #~ skip_until = "'''" 

                        #~ continue 

                    self.count_code += 1 

                else: 

                    self.count_doc += 1 

                    #~ if line.startswith(skip_until): 

                    if skip_until in line: 

                        skip_until = None 

 

        #~ self.count_code, count_total, count_blank, count_doc 

 

 

from lino.utils import rstgen 

 

 

def analyze_rst(*packages): 

    """ 

    Example: 

     

    >>> from lino.utils.code import analyze_rst 

    >>> print analyze_rst('lino') 

       

    """ 

    fields = 'count_code count_doc count_comment count_total'.split() 

    headers = ["name", "code lines", "doc lines", "comment lines", "total lines"] 

    rows = [] 

 

    total_sums = [0] * len(fields) 

    for package in packages: 

        sums = [0] * len(fields) 

        for name, filename in codefiles(package + '*'): 

            sf = SourceFile(name, filename) 

            for i, k in enumerate(fields): 

                sums[i] += getattr(sf, k) 

        rows.append([package] + [str(n) for n in sums]) 

        for i, k in enumerate(fields): 

            total_sums[i] += sums[i] 

    rows.append(['total'] + [str(n) for n in total_sums]) 

    return rstgen.table(headers, rows) 

 

if __name__ == "__main__": 

    import doctest 

    doctest.testmod()