# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.

"""Defines `Context` class, convenient for customizable validation.

Usage:

    def validate(value, ctx=None):
      ctx = ctx or Context.raise_on_error()
      if value['foo'] < 0:
        ctx.error('foo is too low: %d', value['foo'])
      if len(value['bar']) == 0:
        ctx.warning('bar is empty')

    validate({'foo': -1, 'bar': []})  # Will raise ValueError with message.

    ctx = validation.Context.logging()
    validate({'foo': -1, 'bar': []}, ctx)  # Does not raise, but logs messages.

    ctx = validation.Context()  # Generic context
    validate({'foo': -1, 'bar': []}, ctx)
    result = ctx.result()
    # result is an instance of Result. It contains messages with text and
    # severity level.
"""

import collections
import contextlib
import logging

__all__ = [
  'Context',
  'Message',
  'Result',
]

Message = collections.namedtuple('Message', ['text', 'severity'])
ResultBase = collections.namedtuple('ResultBase', ['messages'])


class Result(ResultBase):
  def _has_level(self, severity):
    return any(m.severity >= severity for m in self.messages)

  @property
  def has_errors(self):
    return self._has_level(logging.ERROR)


class Context(object):
  """Collects validation messages.

  A validation message has:
    text (str): text for humans.
    severity (int): severity level. Use standard logging levels.
  """

  def __init__(self, on_message=None):
    """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
    assert on_message is None or hasattr(on_message, '__call__'), on_message
    self.messages = []
    self.on_message = on_message
    self.prefixes = ['']

  @contextlib.contextmanager
  def prefix(self, prefix, *args):
    """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
    new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
    self.prefixes.append(new_prefix)
    try:
      yield
    finally:
      assert self.prefixes.pop() == new_prefix

  def msg(self, severity, text, *args):
    """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
    assert isinstance(severity, int), severity
    assert isinstance(text, basestring), text
    assert severity >= 0, severity
    msg = Message(
        severity=severity,
        text='%s%s' % (self.prefixes[-1], text % args),
    )
    self.messages.append(msg)
    if self.on_message:
      self.on_message(msg)

  def debug(self, *args, **kwargs):
    """Emits a debug message."""
    self.msg(logging.DEBUG, *args, **kwargs)

  def info(self, *args, **kwargs):
    """Emits an info message."""
    self.msg(logging.INFO, *args, **kwargs)

  def warning(self, *args, **kwargs):
    """Emits a warning message."""
    self.msg(logging.WARNING, *args, **kwargs)

  def error(self, *args, **kwargs):
    """Emits an error message."""
    self.msg(logging.ERROR, *args, **kwargs)

  def critical(self, *args, **kwargs):
    """Emits an critical message."""
    self.msg(logging.CRITICAL, *args, **kwargs)

  def result(self):
    """Returns an instance of Result with a copy of the context state."""
    return Result(self.messages[:])

  @classmethod
  def raise_on_error(cls, prefix=None, exc_type=None):
    """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
    exc_type = exc_type or ValueError
    def on_message(msg):
      if msg.severity >= logging.ERROR:
        text = msg.text
        if prefix:
          text = '%s%s' % (prefix, text)
        raise exc_type(text)
    return cls(on_message=on_message)

  @classmethod
  def logging(cls, logger=None):
    """Returns a context that logs all messages."""
    logger = logger or logging.getLogger()
    on_message = lambda msg: logger.log(msg.severity, msg.text)
    return cls(on_message=on_message)

b049a9520b5c1398071e621cc1cb61cb9a2219b1
<mask token>


class Context(object):
    <mask token>

    def __init__(self, on_message=None):
        """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
        assert on_message is None or hasattr(on_message, '__call__'
            ), on_message
        self.messages = []
        self.on_message = on_message
        self.prefixes = ['']

    @contextlib.contextmanager
    def prefix(self, prefix, *args):
        """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
        new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
        self.prefixes.append(new_prefix)
        try:
            yield
        finally:
            assert self.prefixes.pop() == new_prefix

    def msg(self, severity, text, *args):
        """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
        assert isinstance(severity, int), severity
        assert isinstance(text, basestring), text
        assert severity >= 0, severity
        msg = Message(severity=severity, text='%s%s' % (self.prefixes[-1], 
            text % args))
        self.messages.append(msg)
        if self.on_message:
            self.on_message(msg)

    def debug(self, *args, **kwargs):
        """Emits a debug message."""
        self.msg(logging.DEBUG, *args, **kwargs)

    def info(self, *args, **kwargs):
        """Emits an info message."""
        self.msg(logging.INFO, *args, **kwargs)

    def warning(self, *args, **kwargs):
        """Emits a warning message."""
        self.msg(logging.WARNING, *args, **kwargs)

    def error(self, *args, **kwargs):
        """Emits an error message."""
        self.msg(logging.ERROR, *args, **kwargs)

    def critical(self, *args, **kwargs):
        """Emits an critical message."""
        self.msg(logging.CRITICAL, *args, **kwargs)

    def result(self):
        """Returns an instance of Result with a copy of the context state."""
        return Result(self.messages[:])

    @classmethod
    def raise_on_error(cls, prefix=None, exc_type=None):
        """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
        exc_type = exc_type or ValueError

        def on_message(msg):
            if msg.severity >= logging.ERROR:
                text = msg.text
                if prefix:
                    text = '%s%s' % (prefix, text)
                raise exc_type(text)
        return cls(on_message=on_message)

    @classmethod
    def logging(cls, logger=None):
        """Returns a context that logs all messages."""
        logger = logger or logging.getLogger()
        on_message = lambda msg: logger.log(msg.severity, msg.text)
        return cls(on_message=on_message)

<mask token>


class Result(ResultBase):
    <mask token>
    <mask token>


class Context(object):
    """Collects validation messages.

  A validation message has:
    text (str): text for humans.
    severity (int): severity level. Use standard logging levels.
  """

    def __init__(self, on_message=None):
        """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
        assert on_message is None or hasattr(on_message, '__call__'
            ), on_message
        self.messages = []
        self.on_message = on_message
        self.prefixes = ['']

    @contextlib.contextmanager
    def prefix(self, prefix, *args):
        """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
        new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
        self.prefixes.append(new_prefix)
        try:
            yield
        finally:
            assert self.prefixes.pop() == new_prefix

    def msg(self, severity, text, *args):
        """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
        assert isinstance(severity, int), severity
        assert isinstance(text, basestring), text
        assert severity >= 0, severity
        msg = Message(severity=severity, text='%s%s' % (self.prefixes[-1], 
            text % args))
        self.messages.append(msg)
        if self.on_message:
            self.on_message(msg)

    def debug(self, *args, **kwargs):
        """Emits a debug message."""
        self.msg(logging.DEBUG, *args, **kwargs)

    def info(self, *args, **kwargs):
        """Emits an info message."""
        self.msg(logging.INFO, *args, **kwargs)

    def warning(self, *args, **kwargs):
        """Emits a warning message."""
        self.msg(logging.WARNING, *args, **kwargs)

    def error(self, *args, **kwargs):
        """Emits an error message."""
        self.msg(logging.ERROR, *args, **kwargs)

    def critical(self, *args, **kwargs):
        """Emits an critical message."""
        self.msg(logging.CRITICAL, *args, **kwargs)

    def result(self):
        """Returns an instance of Result with a copy of the context state."""
        return Result(self.messages[:])

    @classmethod
    def raise_on_error(cls, prefix=None, exc_type=None):
        """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
        exc_type = exc_type or ValueError

        def on_message(msg):
            if msg.severity >= logging.ERROR:
                text = msg.text
                if prefix:
                    text = '%s%s' % (prefix, text)
                raise exc_type(text)
        return cls(on_message=on_message)

    @classmethod
    def logging(cls, logger=None):
        """Returns a context that logs all messages."""
        logger = logger or logging.getLogger()
        on_message = lambda msg: logger.log(msg.severity, msg.text)
        return cls(on_message=on_message)

<mask token>
__all__ = ['Context', 'Message', 'Result']
Message = collections.namedtuple('Message', ['text', 'severity'])
ResultBase = collections.namedtuple('ResultBase', ['messages'])


class Result(ResultBase):

    def _has_level(self, severity):
        return any(m.severity >= severity for m in self.messages)

    @property
    def has_errors(self):
        return self._has_level(logging.ERROR)


class Context(object):
    """Collects validation messages.

  A validation message has:
    text (str): text for humans.
    severity (int): severity level. Use standard logging levels.
  """

    def __init__(self, on_message=None):
        """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
        assert on_message is None or hasattr(on_message, '__call__'
            ), on_message
        self.messages = []
        self.on_message = on_message
        self.prefixes = ['']

    @contextlib.contextmanager
    def prefix(self, prefix, *args):
        """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
        new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
        self.prefixes.append(new_prefix)
        try:
            yield
        finally:
            assert self.prefixes.pop() == new_prefix

    def msg(self, severity, text, *args):
        """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
        assert isinstance(severity, int), severity
        assert isinstance(text, basestring), text
        assert severity >= 0, severity
        msg = Message(severity=severity, text='%s%s' % (self.prefixes[-1], 
            text % args))
        self.messages.append(msg)
        if self.on_message:
            self.on_message(msg)

    def debug(self, *args, **kwargs):
        """Emits a debug message."""
        self.msg(logging.DEBUG, *args, **kwargs)

    def info(self, *args, **kwargs):
        """Emits an info message."""
        self.msg(logging.INFO, *args, **kwargs)

    def warning(self, *args, **kwargs):
        """Emits a warning message."""
        self.msg(logging.WARNING, *args, **kwargs)

    def error(self, *args, **kwargs):
        """Emits an error message."""
        self.msg(logging.ERROR, *args, **kwargs)

    def critical(self, *args, **kwargs):
        """Emits an critical message."""
        self.msg(logging.CRITICAL, *args, **kwargs)

    def result(self):
        """Returns an instance of Result with a copy of the context state."""
        return Result(self.messages[:])

    @classmethod
    def raise_on_error(cls, prefix=None, exc_type=None):
        """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
        exc_type = exc_type or ValueError

        def on_message(msg):
            if msg.severity >= logging.ERROR:
                text = msg.text
                if prefix:
                    text = '%s%s' % (prefix, text)
                raise exc_type(text)
        return cls(on_message=on_message)

    @classmethod
    def logging(cls, logger=None):
        """Returns a context that logs all messages."""
        logger = logger or logging.getLogger()
        on_message = lambda msg: logger.log(msg.severity, msg.text)
        return cls(on_message=on_message)

<mask token>
import collections
import contextlib
import logging
__all__ = ['Context', 'Message', 'Result']
Message = collections.namedtuple('Message', ['text', 'severity'])
ResultBase = collections.namedtuple('ResultBase', ['messages'])


class Result(ResultBase):

    def _has_level(self, severity):
        return any(m.severity >= severity for m in self.messages)

    @property
    def has_errors(self):
        return self._has_level(logging.ERROR)


class Context(object):
    """Collects validation messages.

  A validation message has:
    text (str): text for humans.
    severity (int): severity level. Use standard logging levels.
  """

    def __init__(self, on_message=None):
        """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
        assert on_message is None or hasattr(on_message, '__call__'
            ), on_message
        self.messages = []
        self.on_message = on_message
        self.prefixes = ['']

    @contextlib.contextmanager
    def prefix(self, prefix, *args):
        """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
        new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
        self.prefixes.append(new_prefix)
        try:
            yield
        finally:
            assert self.prefixes.pop() == new_prefix

    def msg(self, severity, text, *args):
        """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
        assert isinstance(severity, int), severity
        assert isinstance(text, basestring), text
        assert severity >= 0, severity
        msg = Message(severity=severity, text='%s%s' % (self.prefixes[-1], 
            text % args))
        self.messages.append(msg)
        if self.on_message:
            self.on_message(msg)

    def debug(self, *args, **kwargs):
        """Emits a debug message."""
        self.msg(logging.DEBUG, *args, **kwargs)

    def info(self, *args, **kwargs):
        """Emits an info message."""
        self.msg(logging.INFO, *args, **kwargs)

    def warning(self, *args, **kwargs):
        """Emits a warning message."""
        self.msg(logging.WARNING, *args, **kwargs)

    def error(self, *args, **kwargs):
        """Emits an error message."""
        self.msg(logging.ERROR, *args, **kwargs)

    def critical(self, *args, **kwargs):
        """Emits an critical message."""
        self.msg(logging.CRITICAL, *args, **kwargs)

    def result(self):
        """Returns an instance of Result with a copy of the context state."""
        return Result(self.messages[:])

    @classmethod
    def raise_on_error(cls, prefix=None, exc_type=None):
        """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
        exc_type = exc_type or ValueError

        def on_message(msg):
            if msg.severity >= logging.ERROR:
                text = msg.text
                if prefix:
                    text = '%s%s' % (prefix, text)
                raise exc_type(text)
        return cls(on_message=on_message)

    @classmethod
    def logging(cls, logger=None):
        """Returns a context that logs all messages."""
        logger = logger or logging.getLogger()
        on_message = lambda msg: logger.log(msg.severity, msg.text)
        return cls(on_message=on_message)

# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.

"""Defines `Context` class, convenient for customizable validation.

Usage:

    def validate(value, ctx=None):
      ctx = ctx or Context.raise_on_error()
      if value['foo'] < 0:
        ctx.error('foo is too low: %d', value['foo'])
      if len(value['bar']) == 0:
        ctx.warning('bar is empty')

    validate({'foo': -1, 'bar': []})  # Will raise ValueError with message.

    ctx = validation.Context.logging()
    validate({'foo': -1, 'bar': []}, ctx)  # Does not raise, but logs messages.

    ctx = validation.Context()  # Generic context
    validate({'foo': -1, 'bar': []}, ctx)
    result = ctx.result()
    # result is an instance of Result. It contains messages with text and
    # severity level.
"""

import collections
import contextlib
import logging

__all__ = [
  'Context',
  'Message',
  'Result',
]

Message = collections.namedtuple('Message', ['text', 'severity'])
ResultBase = collections.namedtuple('ResultBase', ['messages'])


class Result(ResultBase):
  def _has_level(self, severity):
    return any(m.severity >= severity for m in self.messages)

  @property
  def has_errors(self):
    return self._has_level(logging.ERROR)


class Context(object):
  """Collects validation messages.

  A validation message has:
    text (str): text for humans.
    severity (int): severity level. Use standard logging levels.
  """

  def __init__(self, on_message=None):
    """Initializes a new Context.

    Args:
      on_message: a function that is called whenever a message is emitted.
    """
    assert on_message is None or hasattr(on_message, '__call__'), on_message
    self.messages = []
    self.on_message = on_message
    self.prefixes = ['']

  @contextlib.contextmanager
  def prefix(self, prefix, *args):
    """Adds a prefix to prepend to error messages.

    If called multiple times, prefixes appear in messages in the order this
    method is called.

    Usage:
      with ctx.prefix('foo: '):
        if foo < 0:
          ctx.error('less than zero')  # 'foo: less than zero'
        if foo % 2 == 1:
          ctx.error('must be an even number')  # 'foo: must be an even number'
    """
    new_prefix = '%s%s' % (self.prefixes[-1], prefix % args)
    self.prefixes.append(new_prefix)
    try:
      yield
    finally:
      assert self.prefixes.pop() == new_prefix

  def msg(self, severity, text, *args):
    """Emits a validation message.

    Args:
      severity (int): severity level. Use standard logging levels.
      text (basestring): message text for humans.
      *args: format args for text, like in logging.info.
    """
    assert isinstance(severity, int), severity
    assert isinstance(text, basestring), text
    assert severity >= 0, severity
    msg = Message(
        severity=severity,
        text='%s%s' % (self.prefixes[-1], text % args),
    )
    self.messages.append(msg)
    if self.on_message:
      self.on_message(msg)

  def debug(self, *args, **kwargs):
    """Emits a debug message."""
    self.msg(logging.DEBUG, *args, **kwargs)

  def info(self, *args, **kwargs):
    """Emits an info message."""
    self.msg(logging.INFO, *args, **kwargs)

  def warning(self, *args, **kwargs):
    """Emits a warning message."""
    self.msg(logging.WARNING, *args, **kwargs)

  def error(self, *args, **kwargs):
    """Emits an error message."""
    self.msg(logging.ERROR, *args, **kwargs)

  def critical(self, *args, **kwargs):
    """Emits an critical message."""
    self.msg(logging.CRITICAL, *args, **kwargs)

  def result(self):
    """Returns an instance of Result with a copy of the context state."""
    return Result(self.messages[:])

  @classmethod
  def raise_on_error(cls, prefix=None, exc_type=None):
    """Returns a context that raises an exception on a first error message.

    Args:
      exc_type (type): exception type to raise. Defaults to ValueError.
    """
    exc_type = exc_type or ValueError
    def on_message(msg):
      if msg.severity >= logging.ERROR:
        text = msg.text
        if prefix:
          text = '%s%s' % (prefix, text)
        raise exc_type(text)
    return cls(on_message=on_message)

  @classmethod
  def logging(cls, logger=None):
    """Returns a context that logs all messages."""
    logger = logger or logging.getLogger()
    on_message = lambda msg: logger.log(msg.severity, msg.text)
    return cls(on_message=on_message)
