summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rwxr-xr-xapp.py84
-rwxr-xr-xdatabase.py54
-rwxr-xr-xmain.py171
-rwxr-xr-xparser.py166
-rw-r--r--pyproject.toml3
-rw-r--r--static/images/favicon-16x16.pngbin0 -> 281 bytes
-rw-r--r--static/images/favicon-32x32.pngbin0 -> 561 bytes
-rw-r--r--static/images/powerdebian.gifbin0 -> 904 bytes
-rw-r--r--static/images/vcss-blue.gifbin0 -> 1176 bytes
-rw-r--r--static/index.md17
-rw-r--r--static/robots.txt2
-rw-r--r--static/styles/style.css96
-rw-r--r--templates/template.html.j210
-rw-r--r--uv.lock135
15 files changed, 564 insertions, 181 deletions
diff --git a/.gitignore b/.gitignore
index aa07372..791e527 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
+__pycache__/
+.venv/
+content/
.python-version
-.venv
-static
-content
*.db
+*.sql
diff --git a/app.py b/app.py
new file mode 100755
index 0000000..33082fc
--- /dev/null
+++ b/app.py
@@ -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
new file mode 100644
index 0000000..41b8244
--- /dev/null
+++ b/static/images/favicon-16x16.png
Binary files differ
diff --git a/static/images/favicon-32x32.png b/static/images/favicon-32x32.png
new file mode 100644
index 0000000..b9484ab
--- /dev/null
+++ b/static/images/favicon-32x32.png
Binary files differ
diff --git a/static/images/powerdebian.gif b/static/images/powerdebian.gif
new file mode 100644
index 0000000..1f617c8
--- /dev/null
+++ b/static/images/powerdebian.gif
Binary files differ
diff --git a/static/images/vcss-blue.gif b/static/images/vcss-blue.gif
new file mode 100644
index 0000000..697fbfc
--- /dev/null
+++ b/static/images/vcss-blue.gif
Binary files differ
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>
diff --git a/uv.lock b/uv.lock
index 92f0035..cab9c34 100644
--- a/uv.lock
+++ b/uv.lock
@@ -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 },
]