first commit
This commit is contained in:
3
www/_server_config.py
Normal file
3
www/_server_config.py
Normal file
@@ -0,0 +1,3 @@
|
||||
SERVER_DOWNLOADPY = False
|
||||
SERVER_PORT = 8080
|
||||
SERVER_HOST = '0.0.0.0'
|
||||
60
www/docs/basics.pyp
Normal file
60
www/docs/basics.pyp
Normal file
@@ -0,0 +1,60 @@
|
||||
<py!>
|
||||
import components.code as code
|
||||
import components.docs as docs
|
||||
</py!>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pyp | queries</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="font-sans min-h-[90vh] flex flex-col items-center">
|
||||
<div class="w-full max-w-[80vw] flex gap-5 h-10 bg-slate-900/50 py-5 fixed my-5 p-6 rounded-full backdrop-blur-md lg:max-w-3xl items-center justift-around text-white">
|
||||
<a class="font-bold" href="./">PyWPR docs</a>
|
||||
</div>
|
||||
<div class="inline-flex flex-col gap-5 items-center max-w-[85vw] mt-20">
|
||||
<h1 class="text-4xl">
|
||||
basics
|
||||
</h1>
|
||||
<div class="block h-0.5 bg-zinc-400 w-full">
|
||||
</div>
|
||||
|
||||
<h3 class="text-2xl"><py>! tag</h3>
|
||||
<p class="text-center">in pywrp we have <span class="bg-black p-2 rounded-xl text-white font-mono"><py!></span> and <span class="bg-black p-2 rounded-xl text-white font-mono"></py!></span> HTML tags for embedding python <b>server</b> code,
|
||||
<br><i>(almost like in php)</i><br>
|
||||
for example:</p>
|
||||
<p?>
|
||||
code.code_comp(f"""<p>this is html element</p>
|
||||
{'<'}py!> # This is Python code</py!>""",'html')
|
||||
|
||||
</p?>
|
||||
<div class="block h-0.5 bg-zinc-400 w-full"></div>
|
||||
<h3 class="text-2xl">echo function</h3>
|
||||
<p class="text-center">
|
||||
<span class="bg-black p-2 rounded-xl text-white font-mono">e()</span> is a function that prints text to the page
|
||||
<br><i>(just like <span class="font-mono">echo</span> in php)</i>
|
||||
</p>
|
||||
<p?>code.code_comp(f"""{'<'}py!>
|
||||
e("hello word")
|
||||
# also you can use just regular python print
|
||||
print(object)
|
||||
# e() supports only str|int|float, but you can ignoreType=True
|
||||
e({'{'}'test':123{'}'},ignoreType=True)
|
||||
</py!>""")</p?>
|
||||
<div class="block h-0.5 bg-zinc-400 w-full"></div>
|
||||
<h3 class="text-2xl"><p?> tag</h3>
|
||||
<p class="text-center">also there is <span class="bg-black p-2 rounded-xl text-white font-mono"><p?></p?></span> tags, they are shortcuts to <b>echo</b>
|
||||
<br><i>(like in php too)</i><br>
|
||||
for example:</p>
|
||||
<p?>
|
||||
code.code_comp(f"""{'<'}p?>"hello world"{'<'}/p?>
|
||||
<!--it same as-->
|
||||
{'<'}py!>e("hello world"){'</'}py!>
|
||||
<!--- also supports functions-->
|
||||
{'<'}p?>foo(){'</'}p?>""",'html')
|
||||
</p?>
|
||||
</div></div>
|
||||
</body>
|
||||
</html>
|
||||
166
www/docs/components/code.py
Normal file
166
www/docs/components/code.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import io
|
||||
import html
|
||||
import token
|
||||
import tokenize
|
||||
import keyword
|
||||
import builtins
|
||||
import re
|
||||
import sys
|
||||
|
||||
PALETTE = {
|
||||
'kw': 'text-pink-400', 'builtin': 'text-violet-300', 'name': 'text-sky-300', 'func': 'text-sky-300',
|
||||
'attr': 'text-green-300', 'str': 'text-amber-300', 'num': 'text-cyan-300', 'op': 'text-red-400',
|
||||
'punct': 'text-red-400', 'cmt': 'text-gray-500 italic', 'err': 'bg-red-900 text-red-300',
|
||||
'ht_tag': 'text-fuchsia-300', 'ht_attr': 'text-green-300', 'ht_eq': 'text-red-400',
|
||||
'ht_str': 'text-amber-300', 'ht_comment': 'text-gray-500 italic', 'ht_text': 'text-slate-300'
|
||||
}
|
||||
|
||||
_builtin_names = set(dir(builtins))
|
||||
|
||||
|
||||
def esc(s):
|
||||
return html.escape(s).replace(' ', ' ').replace('\t', ' '*4)
|
||||
|
||||
|
||||
def py_highlight(src):
|
||||
out = []
|
||||
prev = None
|
||||
gen = tokenize.generate_tokens(io.StringIO(src).readline)
|
||||
for ttype, val, *_ in gen:
|
||||
cls = ''
|
||||
if ttype == token.NAME:
|
||||
if keyword.iskeyword(val):
|
||||
cls = 'kw'
|
||||
elif prev and prev[0] == token.OP and prev[1] == '.':
|
||||
cls = 'attr'
|
||||
elif val in _builtin_names:
|
||||
cls = 'builtin'
|
||||
else:
|
||||
cls = 'name'
|
||||
elif ttype == token.OP:
|
||||
cls = 'op' if re.match(r'[+\-*/%=<>!^|&~]', val) else 'punct'
|
||||
elif ttype == token.STRING:
|
||||
cls = 'str'
|
||||
elif ttype == token.NUMBER:
|
||||
cls = 'num'
|
||||
elif ttype == token.COMMENT:
|
||||
cls = 'cmt'
|
||||
elif ttype == token.ERRORTOKEN:
|
||||
cls = 'err'
|
||||
prev = (ttype, val)
|
||||
piece = esc(val).replace('\n', '\n') # keep newlines
|
||||
out.append(
|
||||
f'<span class="{PALETTE.get(cls, "")}">{piece}</span>' if cls else piece)
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
# HTML regexes
|
||||
FULL_TAG = r'<\s*/?\s*[A-Za-z0-9:-]+[!?]?(?:\s[^<>]*?)?>'
|
||||
HT_RE = re.compile(r'(?s)(<!--.*?-->)|(' + FULL_TAG + r')|([^<]+)')
|
||||
TAG_NAME_RE = re.compile(r'(?s)(<\s*/?\s*)([A-Za-z0-9:-]+[!?]?)(.*?)(>)')
|
||||
ATTR_RE = re.compile(r'([A-Za-z0-9:-]+)(\s*=\s*)?', re.S)
|
||||
QSTR_RE = re.compile(r'(".*?"|\'.*?\')', re.S)
|
||||
PY_OPEN = re.compile(r'(?i)^<\s*([A-Za-z0-9:-]+[!?]?)') # capture tag name
|
||||
|
||||
PY_NAMES = {'py!', 'p?'}
|
||||
|
||||
|
||||
def process_fulltag(fulltag):
|
||||
m = TAG_NAME_RE.match(fulltag)
|
||||
if not m:
|
||||
return f'<span class="{PALETTE["ht_tag"]}">{esc(fulltag)}</span>'
|
||||
pre, name, rest, gt = m.groups()
|
||||
out = [esc(pre), f'<span class="{PALETTE["ht_tag"]}">{esc(name)}</span>']
|
||||
if rest:
|
||||
i = 0
|
||||
while i < len(rest):
|
||||
ma = ATTR_RE.match(rest, i)
|
||||
if ma:
|
||||
an, eq = ma.groups()
|
||||
out.append(
|
||||
f'<span class="{PALETTE["ht_attr"]}">{esc(an)}</span>')
|
||||
if eq:
|
||||
out.append(esc(eq))
|
||||
i = ma.end()
|
||||
continue
|
||||
mq = QSTR_RE.match(rest, i)
|
||||
if mq:
|
||||
q = mq.group(1)
|
||||
out.append(
|
||||
f'<span class="{PALETTE["ht_str"]}">{esc(q)}</span>')
|
||||
i = mq.end()
|
||||
continue
|
||||
out.append(esc(rest[i]))
|
||||
i += 1
|
||||
out.append(esc(gt))
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
def _html_tokens(src):
|
||||
out = []
|
||||
i = 0
|
||||
L = len(src)
|
||||
# iterate through matches but manage consumption manually to support multi-line py containers
|
||||
for m in HT_RE.finditer(src):
|
||||
if m.start() < i:
|
||||
continue
|
||||
com, fulltag, text = m.groups()
|
||||
if com:
|
||||
out.append(
|
||||
f'<span class="{PALETTE["ht_comment"]}">{esc(com)}</span>')
|
||||
i = m.end()
|
||||
continue
|
||||
if fulltag:
|
||||
# detect tag name
|
||||
mt = TAG_NAME_RE.match(fulltag)
|
||||
name = mt.group(2) if mt else ''
|
||||
lname = name.lower()
|
||||
is_open = not re.match(r'<\s*/', fulltag)
|
||||
# if opening py-container, find the matching closing tag (first occurrence)
|
||||
if lname in PY_NAMES and is_open:
|
||||
out.append(process_fulltag(fulltag)) # opening
|
||||
# closing tag pattern, case-insensitive
|
||||
close_re = re.compile(rf'(?i)</\s*{re.escape(name)}\s*>')
|
||||
mclose = close_re.search(src, m.end())
|
||||
if mclose:
|
||||
inner = src[m.end():mclose.start()]
|
||||
out.append(py_highlight(inner))
|
||||
out.append(process_fulltag(mclose.group(0))) # closing
|
||||
i = mclose.end()
|
||||
# continue scanning after closing
|
||||
continue
|
||||
else:
|
||||
# no closing found: just output opening and continue
|
||||
i = m.end()
|
||||
continue
|
||||
else:
|
||||
out.append(process_fulltag(fulltag))
|
||||
i = m.end()
|
||||
continue
|
||||
if text:
|
||||
out.append(
|
||||
f'<span class="{PALETTE["ht_text"]}">{esc(text)}</span>')
|
||||
i = m.end()
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
def highlight(src, lang='py'):
|
||||
return _html_tokens(src) if lang and lang.lower().startswith('h') else py_highlight(src)
|
||||
|
||||
|
||||
def code_comp(code, lang='py'):
|
||||
code = code.split('\n')
|
||||
lines = '\n'.join(
|
||||
[f'<span class="before:content-[\'{str(line).zfill(len(str(len(code))))}\'] ">{highlight(code_line, lang)}</span>' for line, code_line in enumerate(code, 1)])
|
||||
return f'''
|
||||
<div class="border border-slate-500 border-4 rounded-xl p-5 bg-gradient-to-br from-zinc-900 to-slate-800 w-full text-center">
|
||||
<div class="flex gap-1.5 *:w-[12px] *:h-[12px] *:bg-white *:rounded-full *:transition pb-3">
|
||||
<span class="hover:bg-red-600"></span>
|
||||
<span class="hover:bg-yellow-300"></span>
|
||||
<span class="hover:bg-green-500"></span>
|
||||
</div>
|
||||
<div class="*:text-white font-mono flex flex-col gap-0 my-2 *:before:mr-3 *:text-sm *:text-left *:before:px-4 *:before:border-r *:before:border-white/20 *:flex">
|
||||
{lines}
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
2
www/docs/components/docs.py
Normal file
2
www/docs/components/docs.py
Normal file
@@ -0,0 +1,2 @@
|
||||
def header_comp(url):
|
||||
pass
|
||||
33
www/docs/index.pyp
Normal file
33
www/docs/index.pyp
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pywpr docs</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="font-sans min-h-[90vh] flex flex-col items-center">
|
||||
<div class="w-full max-w-[80vw] flex gap-5 h-10 bg-slate-900/50 py-5 fixed my-5 p-6 rounded-full backdrop-blur-md lg:max-w-3xl items-center justift-around text-white">
|
||||
<a class="font-bold" href="./">PyWPR docs</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="inline-flex flex-col gap-5 items-center max-w-[85vw] mt-20">
|
||||
<h1 class="text-4xl">
|
||||
PyWPR docs
|
||||
</h1>
|
||||
|
||||
<div class="flex flex-col gap-3 w-full p-5 border rounded-xl bg-gradient-to-br from-green-50 to-purple-100">
|
||||
<div class="flex gap-1.5 *:w-[12px] *:h-[12px] *:bg-slate-800 mb-3 *:rounded-full *:transition ">
|
||||
<span class="hover:bg-red-600"></span>
|
||||
<span class="hover:bg-yellow-300"></span>
|
||||
<span class="hover:bg-green-500"></span>
|
||||
</div>
|
||||
<py!>
|
||||
from pages import pages
|
||||
for i, (name, url) in enumerate(pages):
|
||||
e(f'<a href="{url}" class="text-blue-600 font-mono text-lg">{i+1}. {name}</a>')
|
||||
</py!>
|
||||
</div></div></div>
|
||||
</body>
|
||||
</html>
|
||||
4
www/docs/pages.py
Normal file
4
www/docs/pages.py
Normal file
@@ -0,0 +1,4 @@
|
||||
pages = [
|
||||
['basics', './basics.pyp'],
|
||||
['pyp | queries', './queries.pyp'],
|
||||
]
|
||||
77
www/docs/queries.pyp
Normal file
77
www/docs/queries.pyp
Normal file
@@ -0,0 +1,77 @@
|
||||
<py!>
|
||||
import components.code as code
|
||||
import components.docs as docs
|
||||
</py!>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pyp | queries</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="font-sans min-h-[90vh] flex flex-col items-center">
|
||||
<div class="w-full max-w-[80vw] flex gap-5 h-10 bg-slate-900/50 py-5 fixed my-5 p-6 rounded-full backdrop-blur-md lg:max-w-3xl items-center justift-around text-white">
|
||||
<a class="font-bold" href="./">PyWPR docs</a>
|
||||
</div>
|
||||
<div class="inline-flex flex-col gap-5 items-center max-w-[85vw] lg:max-w-3xl mt-20">
|
||||
<h1 class="text-4xl">
|
||||
pyp | queries
|
||||
</h1>
|
||||
<p class="text-center">in pywrp we use <span class="bg-black p-2 rounded-xl text-white font-mono">pyp.GET</span> object for ?get=queries<br>
|
||||
you can access query parameters like dictionary keys<br>
|
||||
for example:</p>
|
||||
<p?>
|
||||
code.code_comp("""pyp.GET['name']
|
||||
# or
|
||||
pyp.GET.get('name', 'default_value')""")
|
||||
</p?>
|
||||
<p>try it out:</p>
|
||||
<div class="flex flex-col gap-3 w-full p-5 border rounded-xl bg-gradient-to-br from-blue-50 to-red-100">
|
||||
<div class="flex gap-1.5 *:w-[12px] *:h-[12px] *:bg-slate-800 mb-3 *:rounded-full *:transition ">
|
||||
<span class="hover:bg-red-600"></span>
|
||||
<span class="hover:bg-yellow-300"></span>
|
||||
<span class="hover:bg-green-500"></span>
|
||||
</div>
|
||||
<span class="font-mono text-left w-full"><form method="get"></span>
|
||||
<form method="get" class="flex flex-col gap-3 items-center justify-center w-full ">
|
||||
<div class="flex items-center justify-center w-full *:max-w-40">
|
||||
<input type="text" name="name" placeholder="name" class="p-2 border rounded-xl bg-black/40 text-white placeholder-white/60 text-center" required>
|
||||
<span class="flex items-center"><input type="number " name="money" placeholder="your money" class="p-2 border rounded-xl w-full text-center bg-black/40 text-white placeholder-white/60">$</span></div>
|
||||
<button type="submit" class="bg-gradient-to-br from-blue-500 to-blue-300 w-full max-w-40 text-white p-2 rounded-xl">submit</button>
|
||||
</form>
|
||||
<span class="font-mono text-right w-full"></form></span>
|
||||
<py!>
|
||||
if 'name' in pyp.GET and pyp.GET['name'].strip() != "":
|
||||
name = pyp.GET['name']
|
||||
money = pyp.GET.get('money', "")
|
||||
if money != "" and not money.isdigit():
|
||||
money = ""
|
||||
moneyText = f"you have a {'big' if int(money) >= 100 else "little"} money: <span class=\"font-mono\">{money}$</span>!" if money != "" else "you didn't tell me about your money"
|
||||
e(f"<span class=\"w-full bg-zinc-300 h-0.5\"></span><span class=\"font-mono text-left w-full\"><!py></span><div class=\"w-full text-center\">hello, <span class=\"font-mono\">{name}</span>! {moneyText}</div><span class=\"font-mono text-right w-full\"></py!></span>")
|
||||
</py!>
|
||||
</div>
|
||||
|
||||
<p>source</p>
|
||||
<p?>
|
||||
code.code_comp(f"""<form action="get">
|
||||
<input type="text" name="name" placeholder="name">
|
||||
<input type="number" name="money" placeholder="your money">
|
||||
<button type="submit">submit</button>
|
||||
</form>
|
||||
{'<'}py!>
|
||||
if 'name' in pyp.GET and pyp.GET['name'].strip() != "":
|
||||
name = pyp.GET['name']
|
||||
money = pyp.GET.get('money', '')
|
||||
if money != "" and not money.isdigit():
|
||||
money = ""
|
||||
moneyText = f"you have a {'{'}'big' if int(money) >= 100
|
||||
else "little"{'}'} money: {'{'}money{'}'}$!" \\
|
||||
if money != "" else "you didn't tell me about your money"
|
||||
e(f"<div>hello, {'{'}name{'}'}! {'{'}moneyText{'}'}</div>")
|
||||
</py!>""",'html')
|
||||
</p?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
2
www/import_test.py
Normal file
2
www/import_test.py
Normal file
@@ -0,0 +1,2 @@
|
||||
def hello():
|
||||
return "Hello, world!<span style=\"font-size:small\">this was imported from another python script</span>"
|
||||
16
www/import_test.pyp
Normal file
16
www/import_test.pyp
Normal file
@@ -0,0 +1,16 @@
|
||||
<py!>
|
||||
from import_test import hello
|
||||
</py!>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pyd test </title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="font-sans flex items-center justify-center h-screen flex-col gap-5">
|
||||
<div class="flex gap-5 items-center text-6xl">
|
||||
<?p>hello()</p?></div>
|
||||
</body>
|
||||
</html>
|
||||
31
www/index.pyp
Normal file
31
www/index.pyp
Normal file
@@ -0,0 +1,31 @@
|
||||
<py!>
|
||||
import os
|
||||
a = 0
|
||||
FILE = 'aval.txt'
|
||||
if os.path.exists(FILE):
|
||||
try:
|
||||
with open(FILE, "r") as f:
|
||||
a = int(f.read())
|
||||
except Exception:
|
||||
os.remove(FILE)
|
||||
with open(FILE, "w") as f:
|
||||
f.write(str(a + 1))
|
||||
</py!>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pyd test </title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="font-sans flex items-center justify-center h-screen flex-col gap-5">
|
||||
|
||||
<h1 class="text-5xl font-black text-transparent bg-gradient-to-r from-green-500 to-orange-200 bg-clip-text p-2 block">welcome to PyWPR</h1>
|
||||
<div class="text-2xl">a php-like framework<br>get stated by removing <code>www</code> contents and creating <code>index.pyp</code>
|
||||
<br>or go to <a class="text-blue-600" href="./docs/">docs</a></div>
|
||||
<div class="flex gap-5 items-center text-xs">
|
||||
Renders <a href="./" class="text-red-500 font-bold"><p?>a+1</p?></a></div>
|
||||
</body>
|
||||
</html>
|
||||
0
www/redirection_test.pyp
Normal file
0
www/redirection_test.pyp
Normal file
Reference in New Issue
Block a user