def test_option_value_compare(self): # OptionValue are tuple and equivalence should compare as tuples. val = PositiveOptionValue(('foo',)) self.assertEqual(val[0], 'foo') self.assertEqual(val, PositiveOptionValue(('foo',))) self.assertNotEqual(val, PositiveOptionValue(('foo', 'bar'))) # Can compare a tuple to an OptionValue. self.assertEqual(val, ('foo',)) self.assertNotEqual(val, ('foo', 'bar')) # Different OptionValue types are never equal. self.assertNotEqual(val, OptionValue(('foo',))) # For usability reasons, we raise TypeError when attempting to compare # against a non-tuple. with self.assertRaisesRegexp(TypeError, 'cannot compare a'): val == 'foo' # But we allow empty option values to compare otherwise we can't # easily compare value-less types like PositiveOptionValue and # NegativeOptionValue. empty_positive = PositiveOptionValue() empty_negative = NegativeOptionValue() self.assertEqual(empty_positive, ()) self.assertEqual(empty_positive, PositiveOptionValue()) self.assertEqual(empty_negative, ()) self.assertEqual(empty_negative, NegativeOptionValue()) self.assertNotEqual(empty_positive, 'foo') self.assertNotEqual(empty_positive, ('foo',)) self.assertNotEqual(empty_negative, 'foo') self.assertNotEqual(empty_negative, ('foo',))
def run(self, path=None): '''Executes the given file within the sandbox, as well as everything pending from any other included file, and ensure the overall consistency of the executed script(s).''' if path: self.include_file(path) for option in six.itervalues(self._options): # All options must be referenced by some @depends function if option not in self._seen: raise ConfigureError( 'Option `%s` is not handled ; reference it with a @depends' % option.option) self._value_for(option) # All implied options should exist. for implied_option in self._implied_options: value = self._resolve(implied_option.value) if value is not None: # There are two ways to end up here: either the implied option # is unknown, or it's known but there was a dependency loop # that prevented the implication from being applied. option = self._options.get(implied_option.name) if not option: raise ConfigureError( '`%s`, emitted from `%s` line %d, is unknown.' % (implied_option.option, implied_option.caller[1], implied_option.caller[2])) # If the option is known, check that the implied value doesn't # conflict with what value was attributed to the option. option_value = self._value_for_option(option) if value != option_value: reason = implied_option.reason if isinstance(reason, Option): reason = self._raw_options.get(reason) or reason.option reason = reason.split('=', 1)[0] value = OptionValue.from_(value) raise InvalidOptionError( "'%s' implied by '%s' conflicts with '%s' from the %s" % (value.format(option.option), reason, option_value.format( option.option), option_value.origin)) # All options should have been removed (handled) by now. for arg in self._helper: without_value = arg.split('=', 1)[0] msg = 'Unknown option: %s' % without_value if self._help: self._logger.warning(msg) else: raise InvalidOptionError(msg) # Run the execution queue for func, args in self._execution_queue: func(*args) if self._help: with LineIO(self.log_impl.info) as out: self._help.usage(out)
def _value_for_option(self, option): implied = {} for implied_option in self._implied_options[:]: if implied_option.name not in (option.name, option.env): continue self._implied_options.remove(implied_option) if (implied_option.when and not self._value_for(implied_option.when)): continue value = self._resolve(implied_option.value) if value is not None: value = OptionValue.from_(value) opt = value.format(implied_option.option) self._helper.add(opt, 'implied') implied[opt] = implied_option try: value, option_string = self._helper.handle(option) except ConflictingOptionError as e: reason = implied[e.arg].reason if isinstance(reason, Option): reason = self._raw_options.get(reason) or reason.option reason = reason.split('=', 1)[0] raise InvalidOptionError( "'%s' implied by '%s' conflicts with '%s' from the %s" % (e.arg, reason, e.old_arg, e.old_origin)) if value.origin == 'implied': recursed_value = getattr(self, '__value_for_option').get((option,)) if recursed_value is not None: _, filename, line, _, _, _ = implied[value.format(option.option)].caller raise ConfigureError( "'%s' appears somewhere in the direct or indirect dependencies when " "resolving imply_option at %s:%d" % (option.option, filename, line)) if option_string: self._raw_options[option] = option_string when = self._conditions.get(option) # If `when` resolves to a false-ish value, we always return None. # This makes option(..., when='--foo') equivalent to # option(..., when=depends('--foo')(lambda x: x)). if when and not self._value_for(when) and value is not None: # If the option was passed explicitly, we throw an error that # the option is not available. Except when the option was passed # from the environment, because that would be too cumbersome. if value.origin not in ('default', 'environment'): raise InvalidOptionError( '%s is not available in this configuration' % option_string.split('=', 1)[0]) self._logger.log(TRACE, '%r = None', option) return None self._logger.log(TRACE, '%r = %r', option, value) return value