Files
sless/examples/POSTGRES/code/calc-python/handler.py
T

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('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')