text: import os
  from re import compile as re, M
  
  import json
  from tornado import ioloop, log
  
  from .alerts import BaseAlert
  from .utils import parse_interval
  from .handlers import registry
  
  
  LOGGER = log.gen_log
  
  COMMENT_RE = re('//\s+.*$', M)
  
  
  class Reactor(object):
  
      """ Class description. """
  
      defaults = {
          'auth_password': None,
          'auth_username': None,
          'config': 'config.json',
          'critical_handlers': ['log', 'smtp'],
          'format': 'short',
          'debug': False,
          'graphite_url': 'http://localhost',
          'interval': '10minute',
          'repeat_interval': '2hour',
          'logging': 'info',
          'method': 'average',
          'normal_handlers': ['log', 'smtp'],
          'pidfile': None,
          'prefix': '[BEACON]',
          'warning_handlers': ['log', 'smtp'],
      }
  
      def __init__(self, **options):
          self.alerts = set()
          self.loop = ioloop.IOLoop.instance()
          self.options = dict(self.defaults)
          self.reinit(**options)
          self.callback = ioloop.PeriodicCallback(
              self.repeat, parse_interval(self.options['repeat_interval']))
  
      def reinit(self, *args, **options):
          LOGGER.info('Read configuration')
  
          self.options.update(options)
  
          self.include_config(self.options.get('config'))
          for config in self.options.pop('include', []):
              self.include_config(config)
  
          LOGGER.setLevel(self.options.get('logging', 'info').upper())
          registry.clean()
  
          self.handlers = {'warning': set(), 'critical': set(), 'normal': set()}
          self.reinit_handlers('warning')
          self.reinit_handlers('critical')
          self.reinit_handlers('normal')
  
          for alert in list(self.alerts):
              alert.stop()
              self.alerts.remove(alert)
  
          self.alerts = set(
              BaseAlert.get(self, **opts).start() for opts in self.options.get('alerts', []))
  
          LOGGER.debug('Loaded with options:')
          LOGGER.debug(json.dumps(self.options, indent=2))
          return self
  
      def include_config(self, config):
          LOGGER.info('Load configuration: %s' % config)
          if config:
              try:
                  with open(config) as fconfig:
                      source = COMMENT_RE.sub("", fconfig.read())
                      config = json.loads(source)
                      alerts = self.options.get('alerts', [])
                      self.options.update(config)
                      self.options['alerts'] = alerts + self.options.get('alerts', [])
              except (IOError, ValueError):
                  LOGGER.error('Invalid config file: %s' % config)
  
      def reinit_handlers(self, level='warning'):
          for name in self.options['%s_handlers' % level]:
              try:
                  self.handlers[level].add(registry.get(self, name))
              except Exception as e:
                  LOGGER.error('Handler "%s" did not init. Error: %s' % (name, e))
  
      def repeat(self):
          LOGGER.info('Reset alerts')
          for alert in self.alerts:
              alert.reset()
  
      def start(self, *args):
          if self.options.get('pidfile'):
              with open(self.options.get('pidfile'), 'w') as fpid:
                  fpid.write(str(os.getpid()))
          self.callback.start()
          LOGGER.info('Reactor starts')
          self.loop.start()
  
      def stop(self, *args):
          self.callback.stop()
          self.loop.stop()
          if self.options.get('pidfile'):
              os.unlink(self.options.get('pidfile'))
          LOGGER.info('Reactor has stopped')
  
      def notify(self, level, alert, value, target=None, ntype=None):
          if ntype is None:
              ntype = alert.source
  
          for handler in self.handlers[level]:
              handler.notify(level, alert, value, target=target, ntype=ntype)
  