<commit_before>import json
import logging
import os
from time import sleep

import cherrypy
from flask import Response
from flask_restplus import inputs
from pyparsing import Word, Keyword, Group, Forward, Suppress, OneOrMore, oneOf, White, restOfLine, ParseException, \
    Combine
from pyparsing import nums, alphanums, printables

from flexget._version import __version__
from flexget.api import api, APIResource, ApiError, __version__ as __api_version__

log = logging.getLogger('api.server')

server_api = api.namespace('server', description='Manage Daemon')


@server_api.route('/reload/')
class ServerReloadAPI(APIResource):
    @api.response(ApiError, description='Error loading the config')
    @api.response(200, description='Newly reloaded config')
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config()
        except ValueError as e:
            raise ApiError('Error loading config: %s' % e.args[0])

        log.info('Config successfully reloaded from disk.')
        return {}


pid_schema = api.schema('server.pid', {
    'type': 'object',
    'properties': {
        'pid': {
            'type': 'integer'
        }
    }
})


@server_api.route('/pid/')
class ServerPIDAPI(APIResource):
    @api.response(200, description='Reloaded config', model=pid_schema)
    def get(self, session=None):
        """ Get server PID """
        return {'pid': os.getpid()}


shutdown_parser = api.parser()
shutdown_parser.add_argument('force', type=inputs.boolean, required=False, default=False,
                             help='Ignore tasks in the queue')


@server_api.route('/shutdown/')
class ServerShutdownAPI(APIResource):
    @api.doc(parser=shutdown_parser)
    @api.response(200, 'Shutdown requested')
    def get(self, session=None):
        """ Shutdown Flexget Daemon """
        args = shutdown_parser.parse_args()
        self.manager.shutdown(args['force'])
        return {}


@server_api.route('/config/')
class ServerConfigAPI(APIResource):
    @api.response(200, description='Flexget config')
    def get(self, session=None):
        """ Get Flexget Config """
        return self.manager.config


version_schema = api.schema('server.version', {
    'type': 'object',
    'properties': {
        'flexget_version': {'type': 'string'},
        'api_version': {'type': 'integer'}
    }
})


@server_api.route('/version/')
class ServerVersionAPI(APIResource):
    @api.response(200, description='Flexget version', model=version_schema)
    def get(self, session=None):
        """ Flexget Version """
        return {'flexget_version': __version__, 'api_version': __api_version__}


server_log_parser = api.parser()
server_log_parser.add_argument(
    'lines', type=int, required=False, default=200,
    help='How many lines to find before streaming'
)
server_log_parser.add_argument('search', type=str, required=False, help='Search filter support google like syntax')


def reverse_readline(fh, start_byte=0, buf_size=8192):
    """a generator that returns the lines of a file in reverse order"""
    segment = None
    offset = 0
    if start_byte:
        fh.seek(start_byte)
    else:<commit_msg>add dump_threads api for debugging<commit_after>import json
import logging
import os
from time import sleep

import cherrypy
from flask import Response
from flask_restplus import inputs
from pyparsing import Word, Keyword, Group, Forward, Suppress, OneOrMore, oneOf, White, restOfLine, ParseException, \
    Combine
from pyparsing import nums, alphanums, printables

from flexget._version import __version__
from flexget.api import api, APIResource, ApiError, __version__ as __api_version__

log = logging.getLogger('api.server')

server_api = api.namespace('server', description='Manage Daemon')


@server_api.route('/reload/')
class ServerReloadAPI(APIResource):
    @api.response(ApiError, description='Error loading the config')
    @api.response(200, description='Newly reloaded config')
    def get(self, session=None):
        """ Reload Flexget config """
        log.info('Reloading config from disk.')
        try:
            self.manager.load_config()
        except ValueError as e:
            raise ApiError('Error loading config: %s' % e.args[0])

        log.info('Config successfully reloaded from disk.')
        return {}


pid_schema = api.schema('server.pid', {
    'type': 'object',
    'properties': {
        'pid': {
            'type': 'integer'
        }
    }
})


@server_api.route('/pid/')
class ServerPIDAPI(APIResource):
    @api.response(200, description='Reloaded config', model=pid_schema)
    def get(self, session=None):
        """ Get server PID """
        return {'pid': os.getpid()}


shutdown_parser = api.parser()
shutdown_parser.add_argument('force', type=inputs.boolean, required=False, default=False,
                             help='Ignore tasks in the queue')


@server_api.route('/shutdown/')
class ServerShutdownAPI(APIResource):
    @api.doc(parser=shutdown_parser)
    @api.response(200, 'Shutdown requested')
    def get(self, session=None):
        """ Shutdown Flexget Daemon """
        args = shutdown_parser.parse_args()
        self.manager.shutdown(args['force'])
        return {}


@server_api.route('/config/')
class ServerConfigAPI(APIResource):
    @api.response(200, description='Flexget config')
    def get(self, session=None):
        """ Get Flexget Config """
        return self.manager.config


version_schema = api.schema('server.version', {
    'type': 'object',
    'properties': {
        'flexget_version': {'type': 'string'},
        'api_version': {'type': 'integer'}
    }
})


@server_api.route('/version/')
class ServerVersionAPI(APIResource):
    @api.response(200, description='Flexget version', model=version_schema)
    def get(self, session=None):
        """ Flexget Version """
        return {'flexget_version': __version__, 'api_version': __api_version__}


dump_threads_schema = api.schema('server.dump_threads', {
    'type': 'object',
    'properties': {
        'threads': {
            'type': 'array',
            'items': {
                'type': 'object',
                'properties': {
                    'name': {'type': 'string'},
                    'id': {'type': 'string'},
                    'dump': {
                        'type': 'array',
                        'items': {'type': 'string'}
                    }
                },
            },
        }
    }
})


@server_api.route('/dump_threads/')
class ServerDumpThreads(APIResource):
    @api.response(200, description='Flexget threads dump', model=dump_threads_schema)
    def get(self, session=None):
        """ Dump Server threads for debugging """
        import threading, traceback, sys
        id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
        threads = []
        for threadId, stack in sys._current_frames().items():
            dump = []
            for filename, lineno, name, line in traceback.extract_stack(stack):
                dump.append('File: "%s", line %d, in %s' % (filename, lineno, name))
                if line:
                    dump.append(line.strip())
            threads.append({
                'name': id2name.get(threadId),
                'id': threadId,
                'dump': dump
            })

        return {'threads': threads}


server_log_parser = api.parser()
server_log_parser.add_argument(
    'lines', type=int, required=False, default=200,
    help='How many lines to find before streaming'
)
server_log_parser.add_argument('search', type=str, required=False, help='Search filter support google like syntax')


def reverse_readline(fh, start_byte=0, buf_size=8192):
    """a generator that returns the lines of a file in reverse order"""
    segment = None
    offset = 0
    if start_byte:
        fh.seek(start_byte)
    else:
{'fraction_non_alphanumeric': 0.1304896516910651, 'fraction_numerical': 0.0069409389197375064, 'mean_word_length': 3.055783009211873, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4784419', 'n_tokens_mistral': 2374, 'n_tokens_neox': 2278, 'n_words': 570}