106 lines
4.4 KiB
Python
106 lines
4.4 KiB
Python
# Создано: 2026-04-10
|
|
# Изменено: 2026-03-23 — упрощён до поля ввода выражения (демонстрация деплоя).
|
|
# Принимает произвольное математическое выражение: "2+2*(3-1)", "(10/3)**2" и т.д.
|
|
# GET → HTML страница с формой; POST с {expr} → вычисление через безопасный eval.
|
|
# Безопасность eval: __builtins__=None, только math-функции в locals.
|
|
|
|
import math
|
|
|
|
_PAGE = """<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Калькулятор — Python 3.11</title>
|
|
<style>
|
|
body { font-family: monospace; background: #0f172a; color: #e2e8f0;
|
|
display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; }
|
|
.box { background: #1e293b; border-radius: 12px; padding: 32px; width: 420px; box-shadow: 0 8px 32px #0005; }
|
|
h2 { margin: 0 0 4px; font-size: 20px; color: #7dd3fc; }
|
|
.sub { color: #475569; font-size: 12px; margin-bottom: 24px; }
|
|
input { width: 100%; box-sizing: border-box; padding: 10px 14px; font-size: 18px; font-family: monospace;
|
|
background: #0f172a; border: 1px solid #334155; border-radius: 8px; color: #f1f5f9; outline: none; }
|
|
input:focus { border-color: #38bdf8; }
|
|
button { margin-top: 12px; width: 100%; padding: 12px; font-size: 16px; background: #0369a1;
|
|
color: #fff; border: none; border-radius: 8px; cursor: pointer; }
|
|
button:hover { background: #0284c7; }
|
|
button:disabled { background: #1e3a5f; color: #475569; cursor: default; }
|
|
.result { margin-top: 20px; padding: 14px; border-radius: 8px; font-size: 22px; text-align: center; display: none; }
|
|
.ok { background: #064e3b; color: #6ee7b7; display: block; }
|
|
.err { background: #450a0a; color: #fca5a5; font-size: 14px; display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="box">
|
|
<h2>Калькулятор</h2>
|
|
<div class="sub">Python 3.11 · runtime: sless</div>
|
|
<input id="expr" autofocus placeholder="например: 2 + 2 * (3 - 1)">
|
|
<button id="btn" onclick="calc()">Вычислить</button>
|
|
<div id="result" class="result"></div>
|
|
</div>
|
|
<script>
|
|
document.getElementById('expr').addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter') calc();
|
|
});
|
|
async function calc() {
|
|
const expr = document.getElementById('expr').value.trim();
|
|
if (!expr) return;
|
|
const btn = document.getElementById('btn');
|
|
const res = document.getElementById('result');
|
|
btn.disabled = true;
|
|
btn.textContent = '…';
|
|
try {
|
|
const r = await fetch('', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({expr: expr})
|
|
});
|
|
const data = await r.json();
|
|
if (data.error) {
|
|
res.className = 'result err';
|
|
res.textContent = data.error;
|
|
} else {
|
|
res.className = 'result ok';
|
|
res.textContent = expr + ' = ' + data.result;
|
|
}
|
|
} catch(e) {
|
|
res.className = 'result err';
|
|
res.textContent = 'Ошибка сети: ' + e.message;
|
|
}
|
|
btn.disabled = false;
|
|
btn.textContent = 'Вычислить';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>"""
|
|
|
|
# Разрешённые math-функции в eval — без __builtins__ нет доступа к exec/open/etc.
|
|
_MATH_LOCALS = {k: getattr(math, k) for k in dir(math) if not k.startswith('_')}
|
|
|
|
|
|
def handler(event):
|
|
if event.get('_method') == 'POST':
|
|
expr = str(event.get('expr', '')).strip()
|
|
return _compute(expr)
|
|
# GET → HTML страница
|
|
return _PAGE
|
|
|
|
|
|
def _compute(expr):
|
|
if not expr:
|
|
return {'error': 'Введите выражение'}
|
|
try:
|
|
result = eval(expr, {'__builtins__': None}, _MATH_LOCALS) # noqa: S307
|
|
if not isinstance(result, (int, float)):
|
|
return {'error': 'Результат не является числом'}
|
|
return {'expr': expr, 'result': result}
|
|
except ZeroDivisionError:
|
|
return {'error': 'Деление на ноль'}
|
|
except Exception as exc:
|
|
return {'error': f'Ошибка: {exc}'}
|
|
|
|
|
|
def _esc(s):
|
|
# Экранируем HTML-спецсимволы — безопасный вывод в атрибут и тело.
|
|
return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|