From 7185d9b56dbf6dcd06ce208751e9518b8e3ae68c Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 1/8] Move to a (modern?) pyproject.toml --- pyproject.toml | 28 ++++++++++++++++++++++++++++ requirements.txt | 19 ------------------- 2 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b59e404 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[project] +name = "microformats-parser-website" +version = "1.0.0" +description = "A web interface for parsing microformats using mf2py" +readme = "README.md" +license = "CC0-1.0" +requires-python = "==3.9.14" +dependencies = [ + "beautifulsoup4==4.11.1", + "certifi==2022.9.14", + "charset-normalizer==2.1.1", + "click==8.1.3", + "Flask==2.2.2", + "gunicorn==20.1.0", + "html5lib==1.1", + "idna==3.4", + "itsdangerous==2.1.2", + "Jinja2==3.1.2", + "MarkupSafe==2.1.1", + "mf2py==1.1.2", + "mf2util==0.5.1", + "requests==2.28.1", + "six==1.16.0", + "soupsieve==2.3.2.post1", + "urllib3==1.26.12", + "webencodings==0.5.1", + "Werkzeug==2.2.2", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e3bbed6..0000000 --- a/requirements.txt +++ /dev/null @@ -1,19 +0,0 @@ -beautifulsoup4==4.11.1 -certifi==2022.9.14 -charset-normalizer==2.1.1 -click==8.1.3 -Flask==2.2.2 -gunicorn==20.1.0 -html5lib==1.1 -idna==3.4 -itsdangerous==2.1.2 -Jinja2==3.1.2 -MarkupSafe==2.1.1 -mf2py==1.1.2 -mf2util==0.5.1 -requests==2.28.1 -six==1.16.0 -soupsieve==2.3.2.post1 -urllib3==1.26.12 -webencodings==0.5.1 -Werkzeug==2.2.2 From 3bea8f49d5256f98478b683ad5f7e989d4a5b9f0 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 2/8] Remove transitive dependencies --- pyproject.toml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b59e404..e5319a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,23 +6,8 @@ readme = "README.md" license = "CC0-1.0" requires-python = "==3.9.14" dependencies = [ - "beautifulsoup4==4.11.1", - "certifi==2022.9.14", - "charset-normalizer==2.1.1", - "click==8.1.3", - "Flask==2.2.2", + "flask==2.2.2", "gunicorn==20.1.0", - "html5lib==1.1", - "idna==3.4", - "itsdangerous==2.1.2", - "Jinja2==3.1.2", - "MarkupSafe==2.1.1", "mf2py==1.1.2", "mf2util==0.5.1", - "requests==2.28.1", - "six==1.16.0", - "soupsieve==2.3.2.post1", - "urllib3==1.26.12", - "webencodings==0.5.1", - "Werkzeug==2.2.2", ] From f061fff6fa6f2c223f4a2a271302e2fa95f5b1c8 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 3/8] Update Python, move to new Heroku format runtime.txt is deprecated. https://devcenter.heroku.com/articles/python-runtimes --- .python-version | 1 + pyproject.toml | 2 +- runtime.txt | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 .python-version delete mode 100644 runtime.txt diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..c47b345 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14.2 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index e5319a0..7dde3da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "1.0.0" description = "A web interface for parsing microformats using mf2py" readme = "README.md" license = "CC0-1.0" -requires-python = "==3.9.14" +requires-python = "==3.14.2" dependencies = [ "flask==2.2.2", "gunicorn==20.1.0", diff --git a/runtime.txt b/runtime.txt deleted file mode 100644 index 25fd1e5..0000000 --- a/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-3.9.14 \ No newline at end of file From f551f5a6d450cb19ed490a9956129ede88e3f6c2 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 4/8] Update all the dependencies --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7dde3da..c0f6d87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,8 @@ readme = "README.md" license = "CC0-1.0" requires-python = "==3.14.2" dependencies = [ - "flask==2.2.2", - "gunicorn==20.1.0", - "mf2py==1.1.2", - "mf2util==0.5.1", + "flask==3.1.2", + "gunicorn==24.1.1", + "mf2py==2.0.1", + "mf2util==0.5.2", ] From 22a787a568819622ecd404cde96d1b81ed13d6fa Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 5/8] img_with_alt has been removed from mf2py --- app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index c2a370e..7ad0e1a 100644 --- a/app.py +++ b/app.py @@ -38,13 +38,13 @@ def index(): def fetch_mf2(url): if url in cached_mf2: return cached_mf2[url] - p = mf2py.parse(url=url, html_parser=parser or None, img_with_alt = True) + p = mf2py.parse(url=url, html_parser=parser or None) cached_mf2[url] = p return p if url or doc: p = mf2py.parse( - url=url or None, doc=doc or None, html_parser=parser or None, img_with_alt = True + url=url or None, doc=doc or None, html_parser=parser or None ) if util: if any("h-feed" in item["type"] for item in p["items"]): From e90941046c8c8456e6f5811cf71dc287f3c6fb8d Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 6/8] Add lxml, else the app does an automatic fallback --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c0f6d87..08ff6ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ requires-python = "==3.14.2" dependencies = [ "flask==3.1.2", "gunicorn==24.1.1", + "lxml==6.0.2", "mf2py==2.0.1", "mf2util==0.5.2", ] From 0195cfc7f15d34d1167bf2b6c94aa21d72064086 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 7/8] Need to bind localhost explicitly for gunicorn Without an explicit bind it seems to only bind to the 127.0.0.1 IP. To support using CLI options with gunicorn the application cannot also parse the options. Instead only use optparse when running stand-alone. --- README.md | 2 +- app.py | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 40bb79d..7f0933c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Next, start the server. You can do this in either debugging mode (where `debug=T ``` python3 app.py --debug (debug mode) -gunicorn app:app (production mode) +gunicorn --bind localhost:8080 app:app (production mode) ``` You can view your running local application at this URL: diff --git a/app.py b/app.py index 7ad0e1a..808c04c 100644 --- a/app.py +++ b/app.py @@ -1,24 +1,11 @@ import json import traceback from collections import OrderedDict -from optparse import OptionParser import mf2py import mf2util from flask import Flask, jsonify, make_response, render_template, request -parser = OptionParser() - -parser.add_option( - "-d", - "--debug", - action="store_true", - default=False, - help="Run application in debug mode", -) - -(options, args) = parser.parse_args() - app = Flask(__name__) mf2py.Parser.user_agent = "python.microformats.io (mf2py/" + mf2py.__version__ + ") Mozilla/5.0 Chrome/29.0.1547.57 Safari/537.36" @@ -68,6 +55,18 @@ def fetch_mf2(url): traceback.print_exc() return jsonify(error="%s: %s" % (type(e).__name__, e)), 400 +if __name__ == "__main__": + from optparse import OptionParser + + parser = OptionParser() + parser.add_option( + "-d", + "--debug", + action="store_true", + default=False, + help="Run application in debug mode", + ) + (options, args) = parser.parse_args() -if options.debug: - app.run(debug=True, port=8080) + if options.debug: + app.run(debug=True, port=8080) From 14179801326551b79df7ec5a4aae1c3b75418d2f Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Wed, 28 Jan 2026 10:47:17 +0100 Subject: [PATCH 8/8] Honestly I have only tested this with uv ... --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f0933c..4d81456 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ cd microformats-python-parser-website Next, install the required dependencies: ``` -pip3 install -r requirements.txt +uv sync ``` Next, start the server. You can do this in either debugging mode (where `debug=True`) in Flask or in production mode using Gunicorn. ``` -python3 app.py --debug (debug mode) -gunicorn --bind localhost:8080 app:app (production mode) +uv run -- app.py --debug (debug mode) +uv run -- gunicorn --bind localhost:8080 app:app (production mode) ``` You can view your running local application at this URL: