summaryrefslogtreecommitdiff
path: root/coverage/codeunit.py
blob: 3c3d30f5cf9087f78bf3805c2f5c80e0afbe13e6 (plain)
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
"""Code unit (module) handling for coverage.py"""

import glob, os, types

def code_unit_factory(morfs, file_locator, omit_prefixes=None):
    """Construct a list of CodeUnits from polymorphic inputs.
    
    `morfs` is a module or a filename, or a list of same.
    `file_locator` is a FileLocator that can help resolve filenames.
    `omit_prefixes` is a list of prefixes.  CodeUnits that match those prefixes
    will be omitted from the list.
    
    Returns a list of CodeUnit objects.
    
    """

    # Be sure we have a list.
    if not isinstance(morfs, types.ListType):
        morfs = [morfs]
    
    # On Windows, the shell doesn't expand wildcards.  Do it here.
    globbed = []
    for morf in morfs:
        if isinstance(morf, basestring) and ('?' in morf or '*' in morf):
            globbed.extend(glob.glob(morf))
        else:
            globbed.append(morf)
    morfs = globbed

    code_units = [CodeUnit(morf, file_locator) for morf in morfs]
    
    if omit_prefixes:
        prefixes = [file_locator.abs_file(p) for p in omit_prefixes]
        filtered = []
        for cu in code_units:
            for prefix in prefixes:
                if cu.name.startswith(prefix):
                    break
            else:
                filtered.append(cu)
    
        code_units = filtered

    return code_units


class CodeUnit:
    """Code unit: a filename or module.
    
    Instance attributes:
    
    `name` is a human-readable name for this code unit.
    `filename` is the os path from which we can read the source.
    `relative` is a boolean.
    
    """

    def __init__(self, morf, file_locator):
        if hasattr(morf, '__file__'):
            f = morf.__file__
        else:
            f = morf
        # .pyc files should always refer to a .py instead.
        if f.endswith('.pyc'):
            f = f[:-1]
        self.filename = file_locator.canonical_filename(f)

        if hasattr(morf, '__name__'):
            n = morf.__name__
            self.relative = True
        else:
            n = os.path.splitext(morf)[0]
            rel = file_locator.relative_filename(n)
            self.relative = (rel != n)
            n = rel
        self.name = n

    def __cmp__(self, other):
        return cmp(self.name, other.name)

    def flat_rootname(self):
        """A base for a flat filename to correspond to this code unit.
        
        Useful for writing files about the code where you want all the files in
        the same directory, but need to differentiate same-named files from
        different directories.
        
        For example, the file a/b/c.py might return 'a_b_c'
        
        """
        root = os.path.splitdrive(os.path.splitext(self.name)[0])[1]
        return root.replace('\\', '_').replace('/', '_')