summaryrefslogtreecommitdiffstats
path: root/validate.py
blob: 950e0e781cab8f32c56a284ff7f083f8d9f2373e (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
"""
Hooks for validating CPython extension source code
"""
def get_types(strfmt):
    """
    Generate a list of C type names from a PyArg_ParseTuple format string
    Compare to Python/getargs.c:vgetargs1
    """
    result = []
    i = 0
    while i < len(strfmt):
        c = strfmt[i]
        simple = {'i':'int',
                  's':'char *'}
        if c in simple:
            result.append(simple[c] + ' *')
        if c in [':', ';']:
            break
        i += 1
    return result

class CExtensionError(Exception):
    # Base class for errors discovered by static analysis in C extension code
    def __init__(self, location):
        self.location = location

    def __str__(self):
        return '%s:%s: %s' % (self.location.file, 
                              self.location.line,
                              self._get_desc())

    def _get_desc(self):
        raise NotImplementedError
                              

class WrongNumberOfVars(CExtensionError):
    def __init__(self, location, exp_types, actual_types):
        CExtensionError.__init__(self, location)
        self.exp_types = exp_types
        self.actual_types = actual_types

class NotEnoughVars(WrongNumberOfVars):
    def _get_desc(self):
        return 'Not enough arguments: expected %i (%s), but got %i (%s)' % (
            len(self.exp_types),
            self.exp_types,
            len(self.actual_types),
            self.actual_types)

class TooManyVars(WrongNumberOfVars):
    def _get_desc(self):
        return 'Too many arguments: expected %i (%s), but got %i (%s)' % (
            len(self.exp_types),
            self.exp_types,
            len(self.actual_types),
            self.actual_types)

class MismatchingType(CExtensionError):
    def __init__(self, location, arg_num, exp_type, actual_type):
        super(self.__class__, self).__init__(location)
        self.arg_num = arg_num
        self.exp_type = exp_type
        self.actual_type = actual_type

    def _get_desc(self):
        return 'Mismatching type of argument %i: expected "%s" but got "%s"' % (
            self.arg_num,
            self.exp_type,
            self.actual_type)

def validate_types(location, format_string, actual_types):
    try:
        exp_types = get_types(format_string)
        if len(actual_types) < len(exp_types):
            raise NotEnoughVars(location, actual_types, exp_types)
        if len(actual_types) > len(exp_types):
            raise TooManyVars(location, actual_types, exp_types)
        for i, (exp, actual) in enumerate(zip(exp_types, actual_types)):
            if exp != actual:
                raise MismatchingType(location, i+1, exp, actual)
    except CExtensionError, err:
        print err