text: #!/usr/bin/env python
  # -*- coding: utf-8 -*-
  #  2018-11-14 Friedrich Weber <friedrich.weber@netknights.it>
  #             Add a job queue
  #
  # This code is free software; you can redistribute it and/or
  # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  # License as published by the Free Software Foundation; either
  # version 3 of the License, or any later version.
  #
  # This code is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  #
  # You should have received a copy of the GNU Affero General Public
  # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  #
  # Heavily based on huey_consumer.py, which is licensed as follows.
  #
  # Copyright (c) 2017 Charles Leifer
  #
  # Permission is hereby granted, free of charge, to any person obtaining a copy
  # of this software and associated documentation files (the "Software"), to deal
  # in the Software without restriction, including without limitation the rights
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  # copies of the Software, and to permit persons to whom the Software is
  # furnished to do so, subject to the following conditions:
  #
  # The above copyright notice and this permission notice shall be included in
  # all copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  # THE SOFTWARE.
  #
  
  from __future__ import print_function
  import os
  import sys
  
  import copy
  import logging
  from flask import _app_ctx_stack
  from huey.consumer_options import ConsumerConfig, option
  from huey.consumer_options import OptionParserHandler
  
  from privacyidea.app import create_app
  from privacyidea.lib.log import DEFAULT_LOGGING_CONFIG as PI_LOGGING_CONFIG
  from privacyidea.lib.queue import get_job_queue
  from privacyidea.lib.queues.huey_queue import HueyQueue
  
  DEFAULT_LOGFILE = "privacyidea-queue.log"
  
  
  class CustomOptionParserHandler(OptionParserHandler):
      def get_logging_options(self):
          """
          overrides parent method: only supplies ``-l``, not ``-v`` or ``-q``.
          """
          return (
              # -l
              option('logfile', metavar='FILE'),
          )
  
      def get_option_parser(self):
          """
          overrides parent method: modifies the usage string to not include ``path.to.huey_instance``.
          """
          parser = OptionParserHandler.get_option_parser(self)
          parser.set_usage('Usage: %prog [options]')
          return parser
  
  
  def make_push_app_context(app):
      """
      Create a huey pre-execute hook that pushes a new app context before executing any job.
      """
      def func(task):
          ctx = app.app_context()
          ctx.push()
      return func
  
  
  def pop_app_context(task, ret, exc):
      """
      huey post-execute hook that pops the top element of the app context stack.
      """
      ctx = _app_ctx_stack.top
      ctx.pop()
  
  
  def make_logging_config(logfile, loglevel):
      config = copy.deepcopy(PI_LOGGING_CONFIG)
      config["handlers"]["file"]["filename"] = logfile
      config["handlers"]["file"]["level"] = loglevel
      config["loggers"]["privacyidea"]["level"] = loglevel
      config["loggers"]["huey"] = {
          "handlers": ["file"],
          "qualname": "huey",
          "level": loglevel,
      }
      return config
  
  
  def consumer_main():
      app = create_app(config_name='production', silent=True)
  
      with app.app_context():
          queue = get_job_queue()
          if not isinstance(queue, HueyQueue):
              raise RuntimeError("{!r} is not a HueyQueue".format(queue))
  
      parser_handler = CustomOptionParserHandler()
      parser = parser_handler.get_option_parser()
      options, args = parser.parse_args()
  
      options = {k: v for k, v in options.__dict__.items()
                 if v is not None}
      config = ConsumerConfig(**options)
      config.validate()
      # setup logging:
      log_config_file = app.config.get("PI_LOGCONFIG", "/etc/privacyidea/logging.cfg")
      # If the file does not exist, we reconfigure logging.
      if os.path.isfile(log_config_file):
          # If PI_LOGCONFIG points to an existing file, the user is responsible for configuring
          # huey logging in the logconfig file. In this case, we do nothing, because logging
          # has already been set up by ``create_app``.
          # But we ensure that the user hasn't passed ``-l``
          if config.logfile is not None:
              sys.stderr.write("-l is unsupported if PI_LOGCONFIG is used. Exiting.\n")
              sys.exit(1)
          print(u"Using logging configuration from {!r}".format(log_config_file))
      else:
          # If a logfile was given, we write to that logfile. If not, we write to ``privacyidea-queue.log``.
          if config.logfile is not None:
              logfile = config.logfile
          else:
              logfile = DEFAULT_LOGFILE
          loglevel = app.config.get("PI_LOGLEVEL", logging.INFO)
          log_config = make_logging_config(logfile, loglevel)
          logging.config.dictConfig(log_config)
          print(u"Logging to {!r}".format(logfile))
  
      queue.huey.register_pre_execute('push_app_context', make_push_app_context(app))
      queue.huey.register_post_execute('pop_app_context', pop_app_context)
  
      consumer = queue.huey.create_consumer(**config.values)
      consumer.run()
  
  
  if __name__ == '__main__':
      consumer_main()
  