diff options
| -rw-r--r-- | .gitignore | 7 | ||||
| -rwxr-xr-x | app.py | 84 | ||||
| -rwxr-xr-x | database.py | 54 | ||||
| -rwxr-xr-x | main.py | 171 | ||||
| -rwxr-xr-x | parser.py | 166 | ||||
| -rw-r--r-- | pyproject.toml | 3 | ||||
| -rw-r--r-- | static/images/favicon-16x16.png | bin | 0 -> 281 bytes | |||
| -rw-r--r-- | static/images/favicon-32x32.png | bin | 0 -> 561 bytes | |||
| -rw-r--r-- | static/images/powerdebian.gif | bin | 0 -> 904 bytes | |||
| -rw-r--r-- | static/images/vcss-blue.gif | bin | 0 -> 1176 bytes | |||
| -rw-r--r-- | static/index.md | 17 | ||||
| -rw-r--r-- | static/robots.txt | 2 | ||||
| -rw-r--r-- | static/styles/style.css | 96 | ||||
| -rw-r--r-- | templates/template.html.j2 | 10 | ||||
| -rw-r--r-- | uv.lock | 135 |
15 files changed, 564 insertions, 181 deletions
@@ -1,6 +1,7 @@ +__pycache__/ +.venv/ +content/ .python-version -.venv -static -content *.db +*.sql @@ -0,0 +1,84 @@ +from paste.translogger import TransLogger +from waitress import serve + +import datetime +import database +import urllib +import parser +import flask +import sys +import os + +app = flask.Flask(__name__) + +def get_correct_article_headers(db:database.Database, title): + db_headers = list(db.get_header_links()) + if title in [i[0] for i in db_headers]: + out = [] + for i in db_headers: + if i != title: + out.append(i) + return out + [("index", "/~")] + else: + return db_headers + [("index", "/~")] + +def get_template_items(title, db): + return { + "links": db.get_header_links(), + "title": title, + "articles": get_correct_article_headers(db, title) + } + +@app.route("/") +@app.route("/~") +def index(): + with database.Database() as db: + with open(os.path.join("static", "index.md"), "r") as f: + return flask.render_template( + "index.html.j2", + **get_template_items("eva's site", db), + markdown = parser.parse_text(f.read())[0] + ) + +@app.route("/robots.txt") +def robots(): + return flask.send_from_directory("static", "robots.txt") + +@app.route("/thoughts") +def get_thoughts(): + with database.Database() as db: + all_ = db.get_all_thoughts() + return flask.render_template( + "thoughts.html.j2", + **get_template_items("thoughts", db), + ) + +@app.route("/thought") +def get_thought(): + thought_id = flask.request.args.get("id", type=int) + with database.Database() as db: + try: + title, datetime, parsed, headers, redirect = parser.get_thought_from_id(db. thought_id) + except TypeError: + flask.abort(404) + return + + if redirect is not None: + return flask.redirect(redirect, code = 301) + + return flask.render_template( + "thought.html.j2", + **get_template_items(title, db), + md_html = parsed, + contents_html = headers, + datetime = "published: " + str(datetime) + ) + +if __name__ == "__main__": + try: + if sys.argv[1] == "--production": + serve(TransLogger(app), host='0.0.0.0', port = 6969) + else: + app.run(host = "0.0.0.0", port = 5001, debug = True) + except IndexError: + app.run(host = "0.0.0.0", port = 5001, debug = True) diff --git a/database.py b/database.py new file mode 100755 index 0000000..aebd72e --- /dev/null +++ b/database.py @@ -0,0 +1,54 @@ +from dataclasses import dataclass + +import sqlite3 +import datetime + +@dataclass +class Database: + def __enter__(self): + self.__connection = sqlite3.connect("website.db") + return self + + def __exit__(self, type, value, traceback): + self.__connection.commit() + self.__connection.close() + + def get_header_links(self): + cursor = self.__connection.cursor() + cursor.execute("SELECT name, link FROM headerLinks;") + return cursor.fetchall() + + def get_image(self, imageName): + cursor = self.__connection.cursor() + cursor.execute("SELECT alt, url FROM images WHERE imageName = %s;", + (imageName, )) + return cursor.fetchone() + + def get_pfp_images(self): + cursor = self.__connection.cursor() + cursor.execute("SELECT alt, url FROM images WHERE pfp_img = 1;") + return cursor.fetchall() + + def get_sidebar_images(self): + cursor = self.__connection.cursor() + cursor.execute("SELECT alt, url FROM images WHERE sidebar_img = 1;") + return cursor.fetchall() + + def get_all_thoughts(self): + cursor = self.__connection.cursor() + cursor.execute("SELECT id, title, datetime FROM thoughts") + return cursor.fetchall() + + def get_thought(self, id_): + cursor = self.__connection.cursor() + cursor.execute(""" + SELECT title, datetime, markdown_text FROM thoughts + WHERE id = ?;""", (id_, )) + return cursor.fetchall() + + def add_thought(self, title, markdown): + cursor = self.__connection.cursor() + cursor.execute(""" + INSERT INTO thoughts (title, datetime, markdown_text) + VALUES (?, datetime('now'), ?);""", (title, markdown)) + diff --git a/main.py b/main.py deleted file mode 100755 index 1bd3fc9..0000000 --- a/main.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python3 - -from pygments import highlight -from pygments.lexers import get_lexer_by_name -from pygments.formatters import HtmlFormatter -from urllib.parse import urlparse - -import urllib.parse -import lxml.html -import mistune -import houdini -import jinja2 -import shutil -import os - -ENV = jinja2.Environment(loader=jinja2.FileSystemLoader('templates')) - -class MyRenderer(mistune.HTMLRenderer): - def block_code(self, code, info=None): - if not info: - return '\n<pre><code>{}</code></pre>\n'.format(houdini.escape_html(code.strip())) - lexer = get_lexer_by_name(info, stripall=True) - formatter = HtmlFormatter() - return highlight(code, lexer, formatter) - - def block_quote(self, content): - content = content[3:-5] - out = '\n<blockquote>' - for line in houdini.escape_html(content.strip()).split("\n"): - out += '\n<span class="quote">{}</span><br>'.format(line) - return out + '\n</blockquote>' - - def image(self, link, text, title): - return "<a href='%s' target='_blank'><img alt='%s' src='%s'></a>" % ( - urlparse(link)._replace(query='').geturl(), text, link - ) - - def heading(self, text, level): - hash_ = urllib.parse.quote_plus(text) - return "<h%d id='%s'>%s <a class='header_linker' href='#%s'>[#]</a></h%d>\n" % ( - level, hash_, text, hash_, level - ) - -def parse_file(path): - with open(path, "r") as f: - unformatted = f.read() - - return parse_text(unformatted)[0] - -def parse_text(unformatted): - md = mistune.create_markdown( - renderer = MyRenderer(), - plugins = ["strikethrough", "table", "url", "task_lists", "def_list"] - ) - html = md(unformatted) - if html == "": - return "", "" - - return html, get_headers(html) - -def get_headers(html): - root = lxml.html.fromstring(html) - - headers = [] - thesmallestlevel = 7 - for node in root.xpath('//h1|//h2|//h3|//h4|//h5//h6'): - level = int(node.tag[-1]) - if level < thesmallestlevel: - thesmallestlevel = level - headers.append(( - urllib.parse.unquote_plus(node.attrib["id"]), - level, - "#%s" % node.attrib["id"]) - ) - - headers = [(i[0], i[1] - thesmallestlevel, i[2]) for i in headers] - md_template = jinja2.Template(""" - {% for text, depth, link in contents %} - {{ " " * depth }} - [{{ text }}]({{ link }}) - {% endfor %} - """) - - return mistune.html(md_template.render(contents = headers)) - -def index(): - src_file = "content/index.md" - template_file = "index.html.j2" - dst_file = "index.html" - - template = ENV.get_template(template_file) - - with open(os.path.join('static/%s' % dst_file), 'w') as html_file: - with open(src_file, "r") as f: - html = template.render( - prefix = "./", - title = "eva's site", - content = parse_text(f.read())[0] - ) - html_file.write(html) - -def thoughts_overview(): - src_file = "content/thoughts.md" - template_file = "thoughts.html.j2" - dst_file = "thoughts.html" - - template = ENV.get_template(template_file) - - with open(os.path.join('static/%s' % dst_file), 'w') as html_file: - with open(src_file, "r") as f: - html = template.render( - prefix = "./", - title = "my thoughts", - content = parse_text(f.read())[0] - ) - html_file.write(html) - - -def thoughts(): - src_path = "content/thoughts" - template_file = "thought.html.j2" - dst_path = "static/thoughts" - - template = ENV.get_template(template_file) - - if not os.path.isdir(dst_path): - os.mkdir(dst_path) - - for file in os.listdir(src_path): - dst_file = file.replace(".md", ".html") - with open(os.path.join(dst_path, dst_file), 'w') as html_file: - with open(os.path.join(src_path, file), 'r') as f: - html = template.render( - prefix = "../", - title = file.replace("-", " ").replace(".md", ""), - content = parse_text(f.read())[0] - ) - html_file.write(html) - -def about(): - src_file = "content/about.md" - template_file = "about.html.j2" - dst_file = "about.html" - - template = ENV.get_template(template_file) - - with open(os.path.join('static/%s' % dst_file), 'w') as html_file: - with open(src_file, "r") as f: - html = template.render( - prefix = "./", - title = "about me", - content = parse_text(f.read())[0] - ) - html_file.write(html) - - -def main(): - if os.path.isdir("static"): - shutil.rmtree("static") - os.mkdir("static") - - shutil.copytree("content/styles", "static/styles") - shutil.copytree("content/images", "static/images") - shutil.copy("content/robots.txt", "static") - - index() - about() - thoughts_overview() - thoughts() - -if __name__ == '__main__': - main() diff --git a/parser.py b/parser.py new file mode 100755 index 0000000..41ff65b --- /dev/null +++ b/parser.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 + +from pygments import highlight +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter +from urllib.parse import urlparse + +import urllib.parse +import lxml.html +import argparse +import database +import mistune +import houdini +import jinja2 +import parser +import shutil +import sys + +ENV = jinja2.Environment(loader=jinja2.FileSystemLoader('templates')) + +class MyRenderer(mistune.HTMLRenderer): + def block_code(self, code, info=None): + if not info: + return '\n<pre><code>{}</code></pre>\n'.format(houdini.escape_html(code.strip())) + lexer = get_lexer_by_name(info, stripall=True) + formatter = HtmlFormatter() + return highlight(code, lexer, formatter) + + def block_quote(self, content): + content = content[3:-5] + out = '\n<blockquote>' + for line in houdini.escape_html(content.strip()).split("\n"): + out += '\n<span class="quote">{}</span><br>'.format(line) + return out + '\n</blockquote>' + + def image(self, link, text, title): + return "<a href='%s' target='_blank'><img alt='%s' src='%s'></a>" % ( + urlparse(link)._replace(query='').geturl(), text, link + ) + + def heading(self, text, level): + hash_ = urllib.parse.quote_plus(text) + return "<h%d id='%s'>%s <a class='header_linker' href='#%s'>[#]</a></h%d>\n" % ( + level, hash_, text, hash_, level + ) + +def get_thought_from_id(db, id_): + title, datetime, markdown, redirect = db.get_thought(id_) + html, headers = parse_text(markdown) + return title, datetime, html, headers, redirect + +def parse_file(path): + with open(path, "r") as f: + unformatted = f.read() + + return parse_text(unformatted)[0] + +def parse_text(unformatted): + md = mistune.create_markdown( + renderer = MyRenderer(), + plugins = ["strikethrough", "table", "url", "task_lists", "def_list"] + ) + html = md(unformatted) + if html == "": + return "", "" + + return html, get_headers(html) + +def get_headers(html): + root = lxml.html.fromstring(html) + + headers = [] + thesmallestlevel = 7 + for node in root.xpath('//h1|//h2|//h3|//h4|//h5//h6'): + level = int(node.tag[-1]) + if level < thesmallestlevel: + thesmallestlevel = level + headers.append(( + urllib.parse.unquote_plus(node.attrib["id"]), + level, + "#%s" % node.attrib["id"]) + ) + + headers = [(i[0], i[1] - thesmallestlevel, i[2]) for i in headers] + md_template = jinja2.Template(""" + {% for text, depth, link in contents %} + {{ " " * depth }} - [{{ text }}]({{ link }}) + {% endfor %} + """) + + return mistune.html(md_template.render(contents = headers)) + +def main(): + p = argparse.ArgumentParser() + subparse = p.add_subparsers(help = "sub-command help") + save_parser = subparse.add_parser("save", help = "Add a markdown file to the database") + echo_parser = subparse.add_parser("echo", help = "Print markdown render to stdout") + update_parser = subparse.add_parser("update", help = "Replace a markdown file") + export_parser = subparse.add_parser("export", help = "Export a database markdown file to disk") + list_parser = subparse.add_parser("list", help = "List all the markdowns in the database") + + for s in [save_parser, echo_parser, update_parser]: + s.add_argument( + "-m", "--markdown", + help = "Path to a markdown file", + type = str, + required = True + ) + + for s in [save_parser]: + s.add_argument( + "-t", "--title", + help = "Article title", + type = str, + required = True + ) + + for s in [export_parser, update_parser]: + s.add_argument( + "-i", "--id", + help = "Article's id", + type = int, + required = True + ) + + export_parser.add_argument( + "-o", "--out", + help = "Path to write the markdown file to", + type = str, + required = True + ) + + args = vars(p.parse_args()) + + try: + verb = sys.argv[1] + except IndexError: + print("Error! No verb specified. Nothing to do... Exiting...") + exit() + + if verb in ["save", "export", "update", "list"]: + with database.Database() as db: + match verb: + case "save": + with open(args["markdown"], "r") as f: + db.add_thought(args["title"], f.read()) + print("Added thought.") + case "export": + with open(args["out"], "w") as f: + f.writelines(db.get_thought(args["id"])) + print(f"Written to {args["out"]}") + case "update": + with open(args["markdown"], "r") as f: + db.update_thought_markdown(args["id"], f.read()) + print("Updated thought.") + case "list": + for id_, title, datetime in db.get_all_thoughts(): + print(f"{id_}, {title}, {datetime}") + case _: + print("Error! Unknown verb... Exiting...") + exit() + elif verb == "echo": + print(parse_file(args["markdown"])) + +if __name__ == '__main__': + main() diff --git a/pyproject.toml b/pyproject.toml index 86c912b..dc77b1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,12 @@ description = "My website" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "flask>=3.1.0", "houdini-py>=0.1.0", "jinja2==3.1.4", "lxml>=5.3.1", "mistune==3.0.2", + "pastescript==3.2.0", "pygments>=2.19.1", + "waitress>=3.0.2", ] diff --git a/static/images/favicon-16x16.png b/static/images/favicon-16x16.png Binary files differnew file mode 100644 index 0000000..41b8244 --- /dev/null +++ b/static/images/favicon-16x16.png diff --git a/static/images/favicon-32x32.png b/static/images/favicon-32x32.png Binary files differnew file mode 100644 index 0000000..b9484ab --- /dev/null +++ b/static/images/favicon-32x32.png diff --git a/static/images/powerdebian.gif b/static/images/powerdebian.gif Binary files differnew file mode 100644 index 0000000..1f617c8 --- /dev/null +++ b/static/images/powerdebian.gif diff --git a/static/images/vcss-blue.gif b/static/images/vcss-blue.gif Binary files differnew file mode 100644 index 0000000..697fbfc --- /dev/null +++ b/static/images/vcss-blue.gif diff --git a/static/index.md b/static/index.md new file mode 100644 index 0000000..62398c7 --- /dev/null +++ b/static/index.md @@ -0,0 +1,17 @@ +## hi + +I'm a 27 year old graduate in business informatics. I made this website because +I feel like too much of the internet is centered around social media and +sometimes it feels as if the whole internet is only a handful of websites. Years +back I really enjoyed browsing random websites created by people who just wanted +to share their hobby, which made the web feel way more alive and individual than +it is now. I still do this sometimes using [wiby.me](https://wiby.me). Maybe +this website can encourage someone to create their own. +In my spare time I like to go climbing and do some programming on various +projects. Occasionally I'll go on a walk in a random place I've never been in +and take pictures. Maybe I'll add a photo album sometime later. You can read +more about me [here](./about.html) + +## thoughts + +I'll sometimes post my thoughts. You can read them [here](./thoughts.html). diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/static/styles/style.css b/static/styles/style.css new file mode 100644 index 0000000..f4b782f --- /dev/null +++ b/static/styles/style.css @@ -0,0 +1,96 @@ +html { + background-color: #f1f1f1; + font-family: 'Open Sans', sans-serif; + font-size: small; +} + +body { + background: #f1f1f1; + margin-left: 0px; + margin-right: 0px; + margin-bottom: 0px; + position: relative; +} + +footer { + padding: 300px 10px 5px 20px; + margin-bottom: 300px; + font-size: xx-small; + align-content: center; +} + +#headerflex { + display: flex; +} + +#headers { + flex-grow: 1; +} + +header nav { + flex-grow: 1; + display: inline; +} + +header div { + padding-left: 20px; + padding-bottom: 20px; +} + +nav ul { + margin: 0; + padding: 0; +} + +nav li { + display: inline-block; + list-style-type: none; +} + +nav a { + text-decoration: none; + display: block; + padding: 5px 6px 5px 6px; + color: black; +} + +#the_title { + text-decoration: none; + color: black; +} + +#the_title h1 { + padding-left: 6px; +} + +#navbar { + text-align: left; + background-color: white; +} + +#footer_banners { + display: flex; + align-items: center; +} + +#wrapper { + background-color: #f1e7d5; + max-width: 974px; + min-width: 850px; + margin: auto; + margin-top: -10px; + padding: 30px; +} + +#thoughts_list ul { + padding-left: 12px; + line-height: 20px; +} + +#thoughts_list ul li { + list-style: none; +} + +.header_linker { + font-size: x-small; +} diff --git a/templates/template.html.j2 b/templates/template.html.j2 index 4753dfd..711cbc7 100644 --- a/templates/template.html.j2 +++ b/templates/template.html.j2 @@ -17,15 +17,11 @@ <a href="/" id="the_title"><h1>{{ title }}</h1></a> <nav id="navbar"> <ul> + {% for name, link in articles %} <li> - <a href="{{ prefix }}index.html">home</a> - </li> - <li> - <a href="{{ prefix }}thoughts.html">thoughts</a> - </li> - <li> - <a href="{{ prefix }}about.html">about me</a> + <a href="{{link}}">{{name}}</a> </li> + {% endfor %} </ul> </nav> </div> @@ -3,12 +3,67 @@ revision = 1 requires-python = ">=3.13" [[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "flask" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 }, +] + +[[package]] name = "houdini-py" version = "0.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/eb/c1/25aa4ed20e108a2d82efd3787c51457a2468bed382ebec7cd0a1fd5e8d9b/houdini.py-0.1.0.tar.gz", hash = "sha256:cabd10734a859c34c3707ecb99b93cb78547a9128cbfabbe3f4d5628e822f666", size = 15817 } [[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, +] + +[[package]] name = "jinja2" version = "3.1.4" source = { registry = "https://pypi.org/simple" } @@ -83,6 +138,41 @@ wheels = [ ] [[package]] +name = "paste" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/1c/6bc9040bf9b4cfc9334f66d2738f952384c106c48882adf6097fed3da966/paste-3.10.1.tar.gz", hash = "sha256:1c3d12065a5e8a7a18c0c7be1653a97cf38cc3e9a5a0c8334a9dd992d3a05e4a", size = 652629 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/14/032895c25726a859bf48b8ed68944c3efc7a3decd920533ed929f12f08a1/Paste-3.10.1-py3-none-any.whl", hash = "sha256:995e9994b6a94a2bdd8bd9654fb70ca3946ffab75442468bacf31b4d06481c3d", size = 289253 }, +] + +[[package]] +name = "pastedeploy" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/97/0c4a613ec96a54d21daa7e089178263915554320402e89b4e319436a63cb/PasteDeploy-3.1.0.tar.gz", hash = "sha256:9ddbaf152f8095438a9fe81f82c78a6714b92ae8e066bed418b6a7ff6a095a95", size = 37841 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/30/cdddd9a88969683a59222a6d61cd6dce923977f2e9f9ffba38e1324149cd/PasteDeploy-3.1.0-py3-none-any.whl", hash = "sha256:76388ad53a661448d436df28c798063108f70e994ddc749540d733cdbd1b38cf", size = 16943 }, +] + +[[package]] +name = "pastescript" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "paste" }, + { name = "pastedeploy" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/47/45c6f5a3cb8f5abf786fea98dbb8d02400a55768a9b623afb7df12346c61/PasteScript-3.2.0.tar.gz", hash = "sha256:9b0f5c0f1c6a510a353fa7c3dc4fdaab9071462d60d24573de76a001fbc172ac", size = 118495 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/cb/90e367daf2685587665158092b4a78a26ba45b653cc9ca90175bc43b94ad/PasteScript-3.2.0-py2.py3-none-any.whl", hash = "sha256:a08970fdcd5fcd92d79c51fe356d7866f13d30e847bd2eed0351f9644236b3b8", size = 73642 }, +] + +[[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } @@ -92,22 +182,67 @@ wheels = [ ] [[package]] +name = "setuptools" +version = "76.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/d2/7b171caf085ba0d40d8391f54e1c75a1cda9255f542becf84575cfd8a732/setuptools-76.0.0.tar.gz", hash = "sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4", size = 1349387 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/66/d2d7e6ad554f3a7c7297c3f8ef6e22643ad3d35ef5c63bf488bc89f32f31/setuptools-76.0.0-py3-none-any.whl", hash = "sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6", size = 1236106 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "waitress" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232 }, +] + +[[package]] name = "website" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "flask" }, { name = "houdini-py" }, { name = "jinja2" }, { name = "lxml" }, { name = "mistune" }, + { name = "pastescript" }, { name = "pygments" }, + { name = "waitress" }, ] [package.metadata] requires-dist = [ + { name = "flask", specifier = ">=3.1.0" }, { name = "houdini-py", specifier = ">=0.1.0" }, { name = "jinja2", specifier = "==3.1.4" }, { name = "lxml", specifier = ">=5.3.1" }, { name = "mistune", specifier = "==3.0.2" }, + { name = "pastescript", specifier = "==3.2.0" }, { name = "pygments", specifier = ">=2.19.1" }, + { name = "waitress", specifier = ">=3.0.2" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 }, ] |
