1#!/usr/bin/env python
2
3# Copyright (c) 2010, Google Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
10#     * Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12#     * Redistributions in binary form must reproduce the above
13# copyright notice, this list of conditions and the following disclaimer
14# in the documentation and/or other materials provided with the
15# distribution.
16#     * Neither the name of Google Inc. nor the names of its
17# contributors may be used to endorse or promote products derived from
18# this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32"""Module to enforce different constraints on flags.
33
34A validator represents an invariant, enforced over a one or more flags.
35See 'FLAGS VALIDATORS' in gflags.py's docstring for a usage manual.
36"""
37
38__author__ = 'olexiy@google.com (Olexiy Oryeshko)'
39
40
41class Error(Exception):
42  """Thrown If validator constraint is not satisfied."""
43
44
45class Validator(object):
46  """Base class for flags validators.
47
48  Users should NOT overload these classes, and use gflags.Register...
49  methods instead.
50  """
51
52  # Used to assign each validator an unique insertion_index
53  validators_count = 0
54
55  def __init__(self, checker, message):
56    """Constructor to create all validators.
57
58    Args:
59      checker: function to verify the constraint.
60        Input of this method varies, see SimpleValidator and
61          DictionaryValidator for a detailed description.
62      message: string, error message to be shown to the user
63    """
64    self.checker = checker
65    self.message = message
66    Validator.validators_count += 1
67    # Used to assert validators in the order they were registered (CL/18694236)
68    self.insertion_index = Validator.validators_count
69
70  def Verify(self, flag_values):
71    """Verify that constraint is satisfied.
72
73    flags library calls this method to verify Validator's constraint.
74    Args:
75      flag_values: gflags.FlagValues, containing all flags
76    Raises:
77      Error: if constraint is not satisfied.
78    """
79    param = self._GetInputToCheckerFunction(flag_values)
80    if not self.checker(param):
81      raise Error(self.message)
82
83  def GetFlagsNames(self):
84    """Return the names of the flags checked by this validator.
85
86    Returns:
87      [string], names of the flags
88    """
89    raise NotImplementedError('This method should be overloaded')
90
91  def PrintFlagsWithValues(self, flag_values):
92    raise NotImplementedError('This method should be overloaded')
93
94  def _GetInputToCheckerFunction(self, flag_values):
95    """Given flag values, construct the input to be given to checker.
96
97    Args:
98      flag_values: gflags.FlagValues, containing all flags.
99    Returns:
100      Return type depends on the specific validator.
101    """
102    raise NotImplementedError('This method should be overloaded')
103
104
105class SimpleValidator(Validator):
106  """Validator behind RegisterValidator() method.
107
108  Validates that a single flag passes its checker function. The checker function
109  takes the flag value and returns True (if value looks fine) or, if flag value
110  is not valid, either returns False or raises an Exception."""
111  def __init__(self, flag_name, checker, message):
112    """Constructor.
113
114    Args:
115      flag_name: string, name of the flag.
116      checker: function to verify the validator.
117        input  - value of the corresponding flag (string, boolean, etc).
118        output - Boolean. Must return True if validator constraint is satisfied.
119          If constraint is not satisfied, it should either return False or
120          raise Error.
121      message: string, error message to be shown to the user if validator's
122        condition is not satisfied
123    """
124    super(SimpleValidator, self).__init__(checker, message)
125    self.flag_name = flag_name
126
127  def GetFlagsNames(self):
128    return [self.flag_name]
129
130  def PrintFlagsWithValues(self, flag_values):
131    return 'flag --%s=%s' % (self.flag_name, flag_values[self.flag_name].value)
132
133  def _GetInputToCheckerFunction(self, flag_values):
134    """Given flag values, construct the input to be given to checker.
135
136    Args:
137      flag_values: gflags.FlagValues
138    Returns:
139      value of the corresponding flag.
140    """
141    return flag_values[self.flag_name].value
142
143
144class DictionaryValidator(Validator):
145  """Validator behind RegisterDictionaryValidator method.
146
147  Validates that flag values pass their common checker function. The checker
148  function takes flag values and returns True (if values look fine) or,
149  if values are not valid, either returns False or raises an Exception.
150  """
151  def __init__(self, flag_names, checker, message):
152    """Constructor.
153
154    Args:
155      flag_names: [string], containing names of the flags used by checker.
156      checker: function to verify the validator.
157        input  - dictionary, with keys() being flag_names, and value for each
158          key being the value of the corresponding flag (string, boolean, etc).
159        output - Boolean. Must return True if validator constraint is satisfied.
160          If constraint is not satisfied, it should either return False or
161          raise Error.
162      message: string, error message to be shown to the user if validator's
163        condition is not satisfied
164    """
165    super(DictionaryValidator, self).__init__(checker, message)
166    self.flag_names = flag_names
167
168  def _GetInputToCheckerFunction(self, flag_values):
169    """Given flag values, construct the input to be given to checker.
170
171    Args:
172      flag_values: gflags.FlagValues
173    Returns:
174      dictionary, with keys() being self.lag_names, and value for each key
175        being the value of the corresponding flag (string, boolean, etc).
176    """
177    return dict([key, flag_values[key].value] for key in self.flag_names)
178
179  def PrintFlagsWithValues(self, flag_values):
180    prefix = 'flags '
181    flags_with_values = []
182    for key in self.flag_names:
183      flags_with_values.append('%s=%s' % (key, flag_values[key].value))
184    return prefix + ', '.join(flags_with_values)
185
186  def GetFlagsNames(self):
187    return self.flag_names
188