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
|
from abc import abstractmethod
import os
from gitlint.utils import ustr, sstr
class RuleOptionError(Exception):
pass
class RuleOption(object):
""" Base class representing a configurable part (i.e. option) of a rule (e.g. the max-length of the title-max-line
rule).
This class should not be used directly. Instead, use on the derived classes like StrOption, IntOption to set
options of a particular type like int, str, etc.
"""
def __init__(self, name, value, description):
self.name = ustr(name)
self.description = ustr(description)
self.value = None
self.set(value)
@abstractmethod
def set(self, value):
""" Validates and sets the option's value """
pass # pragma: no cover
def __str__(self):
return sstr(self) # pragma: no cover
def __unicode__(self):
return u"({0}: {1} ({2}))".format(self.name, self.value, self.description) # pragma: no cover
def __repr__(self):
return self.__str__() # pragma: no cover
def __eq__(self, other):
return self.name == other.name and self.description == other.description and self.value == other.value
def __ne__(self, other):
return not self.__eq__(other) # required for py2
class StrOption(RuleOption):
def set(self, value):
self.value = ustr(value)
class IntOption(RuleOption):
def __init__(self, name, value, description, allow_negative=False):
self.allow_negative = allow_negative
super(IntOption, self).__init__(name, value, description)
def _raise_exception(self, value):
if self.allow_negative:
error_msg = u"Option '{0}' must be an integer (current value: '{1}')".format(self.name, value)
else:
error_msg = u"Option '{0}' must be a positive integer (current value: '{1}')".format(self.name, value)
raise RuleOptionError(error_msg)
def set(self, value):
try:
self.value = int(value)
except ValueError:
self._raise_exception(value)
if not self.allow_negative and self.value < 0:
self._raise_exception(value)
class BoolOption(RuleOption):
def set(self, value):
value = ustr(value).strip().lower()
if value not in ['true', 'false']:
raise RuleOptionError(u"Option '{0}' must be either 'true' or 'false'".format(self.name))
self.value = value == 'true'
class ListOption(RuleOption):
""" Option that is either a given list or a comma-separated string that can be splitted into a list when being set.
"""
def set(self, value):
if isinstance(value, list):
the_list = value
else:
the_list = ustr(value).split(",")
self.value = [ustr(item.strip()) for item in the_list if item.strip() != ""]
class PathOption(RuleOption):
""" Option that accepts either a directory or both a directory and a file. """
def __init__(self, name, value, description, type=u"dir"):
self.type = type
super(PathOption, self).__init__(name, value, description)
def set(self, value):
value = ustr(value)
error_msg = u""
if self.type == 'dir':
if not os.path.isdir(value):
error_msg = u"Option {0} must be an existing directory (current value: '{1}')".format(self.name, value)
elif self.type == 'file':
if not os.path.isfile(value):
error_msg = u"Option {0} must be an existing file (current value: '{1}')".format(self.name, value)
elif self.type == 'both':
if not os.path.isdir(value) and not os.path.isfile(value):
error_msg = (u"Option {0} must be either an existing directory or file "
u"(current value: '{1}')").format(self.name, value)
else:
error_msg = u"Option {0} type must be one of: 'file', 'dir', 'both' (current: '{1}')".format(self.name,
self.type)
if error_msg:
raise RuleOptionError(error_msg)
self.value = os.path.realpath(value)
|