Предотвращение высчитывания слишком длинных чисел внутри модуля на PyQt5

Я пишу встроенный калькулятор на PyQt5 и хочу предотвратить зависание от больших чисел. Сам калькулятор импортируется как Calculator из файла calculator.py в main.py. Я пробовал следущее в calculator.py:

import multiprocessing as mp
from sympy import sympify
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QGridLayout, QPushButton
import sys

# ...

def timeout_risk(expr):
    return sympify(expr, rational=True)

class Calculator(QWidget):
    def __init__(self):
        super().__init__()
        self.output = QLabel('')
        btns = ['2','+', '=']
        l = QGridLayout(self)
        l.addWidget(self.output, 0, 1)
        for i in range(len(btns)):
            l.addWidget(QPushButton(btns[i], clicked=self.on_click), 1, i)

    def on_click(self):
        sender = self.sender()
        if sender.text() in ('2','+'):
            self.output.setText(self.output.text()+sender.text())
        elif sender.text() == '=':
            self.calculate(self.output.text())

    def calculate(self, txt):
        if mp.current_process().name == 'MainProcess':
            pool = mp.Pool(1)
            async_result = pool.apply_async(timeout_risk, (txt,))

            res = async_result.get(timeout=2)
            self.output.setText(res)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Calculator()
    win.show()
    app.exec_()

Но такое решение замедляет программу если pool не в 'main'. Существуют ли другие доступные способы оптимизировать скорость и решить эту проблему на Windows?

P.S. Обработчики TimeoutError в коде есть

Upd: Добавил демо замедления, при импорте я естественно не могу использовать часть 'main'


Ответы (2 шт):

Автор решения: Seffel

Я если честно не очень понял суть вопроса, если вам нужно ограничить дланну вывода, то можно просто же полученый результат измерить, точнее получить длинну вычисленной строки и если она больше предположим 10 символов, то вывести оповещение о том что число слишком большое. Если вам нужно ограничить время расчёта, то расчёт можно запустить в потоке и если время работы потока больше чем требуется, принудительно завершить поток и в результат вывести ошибку. Я правильно понял ваш вопрос или нет?

→ Ссылка
Автор решения: Stanislav Volodarskiy

Такая архитектура: отдельный процесс-вычислитель, который выполняет sympify. Qt UI общается с вычислителем через две очереди: expressions на вход, results на выход. В нормальном случае цикл обработки очень короткий: запускать новый процесс на каждый пример не нужно. Но если UI в течении двух секунд не получил ответ, он убивает повисший вычислитель и запускает новый. Тут появляются временные затраты, но они не велики по сравнению с двухсекундным ожиданием.

Ниже крохотное демо, которое читает выражения со стандартного входа и вычисляет его. Если вычисление не уложилось в две секунды, пользователю сообщают об ошибке, убивают старый вычислитель, запускают новый. Всё повторяется незаметно для пользователя:

import multiprocessing
import queue
import sympy
import sys


def computer(expressions, results):
    sys.set_int_max_str_digits(0)
    while True:
        expr = expressions.get()
        result = str(sympy.sympify(expr, rational=True))
        results.put(result)


if __name__ == '__main__':
    while True:
        expressions = multiprocessing.Queue()
        results = multiprocessing.Queue()
        p = multiprocessing.Process(target=computer, args=(expressions, results))
        p.start()
        while True:
            expressions.put(input('> '))
            try:
                result = results.get(block=True, timeout=2)
            except queue.Empty:
                p.terminate()
                print('ERROR: timeout')
                break
            print('=', result)
$ python calc.py
> 2 + 2
= 4
> 9 ** 9 ** 9
ERROR: timeout
> 2 + 2
= 4
>
→ Ссылка