import re from templater import render_template from utils import parse_cookies, add_text_headers, SUCCESS, BAD_REQUEST, NOT_FOUND, HTTP_METHODS, METHOD_NOT_ALLOWED from config import TEXT_TEMPLATE_NAME import db _router_tree = {} # url_format - регулярное выражение def route(url_format, methods=None): if methods is None: methods = ['GET'] def wrapper(func): def inner(url, query, *args, **kwargs): pattern = re.compile(url_format) match = re.fullmatch(pattern, url) if match is None or len(match.groups()) != pattern.groups: return BAD_REQUEST, '400 BAD REQUEST' return func(query, *match.groups(), *args, **kwargs) # Добавляем вызываемую функцию в дерево роутера. # Благодаря этому указывать паттерн url и метод нужно указывать # только в инициализаторе декоратора, а функция run сама разберется # при каких условиях нужно вызвать конкретную функцию _router_tree[url_format] = _router_tree.get(url_format, {}) for method in methods: _router_tree[url_format][method] = inner return inner return wrapper def run(request): res = NOT_FOUND, NOT_FOUND for key, value in request['cookies'].items(): db.set_cookie(key, value) method, url = request['method'], request['url'] for url_pattern in _router_tree: if re.fullmatch(url_pattern, url) and method in _router_tree[url_pattern]: res = _router_tree[url_pattern][method](url, request['query']) break return add_text_headers(*res) # По заданию любой метод, кроме GET и POST должны быть запрещены на сервере. @route('/.*', list(set(HTTP_METHODS) ^ {'GET', 'POST'})) def fallback_wrong_method(query, *args): return METHOD_NOT_ALLOWED, 'This method is not allowed' @route('/') def index_get(query, *args): return SUCCESS, render_template('form', color=get_color()) # Хотелось попробовать сделать что-то высокоуровневое с декораторами. # Если в этом коде есть какие-то серьезные проблемы, то скажите сразу. @route(r'/div/(\d+)/to/(\d+)/?') def divide_get(query, *args): color = get_color() try: text = str(int(args[0]) / int(args[1])) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, text=text, color=color) except ZeroDivisionError as e: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + str(e) if db.get_config_entry('show_errors') else '') ) @route(r'/div/?', ['POST']) def divide_post(query, *args): color = get_color() try: text = str(int(query['numerator']) / int(query['denominator'])) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, text=text, color=color) except KeyError: field = 'числитель' if 'numerator' not in query else 'знаменатель' return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + f'Указан неверный {field}') ) except (ValueError, ZeroDivisionError) as e: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + str(e) if db.get_config_entry('show_errors') else '') ) @route(r'/show_errors/(\d{1})/?') def show_errors_get(query, *args): color = get_color() if args[0] == '1': db.set_config_entry('show_errors', 1) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors включена') elif args[0] == '0': db.set_config_entry('show_errors', 0) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors выключена') else: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors не может принимать такое значение' ) @route(r'/show_errors/?', ['POST']) def show_errors_post(query, *args): color = get_color() if 'show_errors' not in query: db.set_config_entry('show_errors', 0) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors выключена') elif query['show_errors'] == '1': db.set_config_entry('show_errors', 1) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors включена') else: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text='Опция show_errors не может принимать такое значение' ) @route(r'/set_cookie/=/(.*)/?') def set_cookie_get(query, *args): cookie_line = args[0] color = get_color() try: cookies = parse_cookies(cookie_line) for key, value in cookies.items(): db.set_cookie(key, value) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Cookie-файл обновлен') except ValueError as e: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + str(e) if db.get_config_entry('show_errors') else '') ) @route(r'/set_cookie/=/?', ['POST']) def set_cookie_post(query, *args): color = get_color() try: cookie_line = query['cookie_line'] cookies = parse_cookies(cookie_line) for key, value in cookies.items(): db.set_cookie(key, value) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Cookie-файл обновлен') except KeyError: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + f'Не указана строка с cookie') ) except ValueError as e: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text=BAD_REQUEST + ('
' + str(e) if db.get_config_entry('show_errors') else '') ) @route(r'/short_log/(\d{1})/?') def short_log_get(query, *args): color = get_color() if args[0] == '1': db.set_config_entry('short_log', 1) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция short_log включена') elif args[0] == '0': db.set_config_entry('short_log', 0) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция short_log выключена') else: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text='Опция short_log не может принимать такое значение' ) @route(r'/short_log/?', ['POST']) def short_log_post(query, *args): color = get_color() if 'short_log' not in query: db.set_config_entry('short_log', 0) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция short_log выключена') elif query['short_log'] == '1': db.set_config_entry('short_log', 1) return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Опция short_log включена') else: return BAD_REQUEST, render_template( TEXT_TEMPLATE_NAME, color=color, text='Опция short_log не может принимать такое значение' ) def get_color(): color = db.get_cookie('bg_color', 'white') if color not in ['green', 'white']: color = 'white' return color