src layout: move core code into src/tinyweb/ package

- Moved app.py, db.py, gateway.py, templates.py, embeddings.py,
  rns_client.py, and handlers/ into src/tinyweb/
- Created root app.py shim (adds src/ to sys.path, imports main)
- Created pyproject.toml with setuptools config (where = ["src"])
- Added src/tinyweb/__init__.py
- Updated all internal imports to use tinyweb. prefix (73 occurrences)
- Removed sys.path.insert hack from conftest.py
- Updated Dockerfile: pip install -e /app before running
- Updated gateway.py usage message: python -m tinyweb.gateway
- Updated README.md gateway usage instructions
This commit is contained in:
blankie 2026-06-17 05:03:23 +00:00
parent afbd3c89c1
commit 0669aaf3a0
35 changed files with 400 additions and 384 deletions

View file

@ -4,8 +4,8 @@ Every POST handler calls this to verify the submitted _csrf field matches
the token stored in the thread-local (which is seeded from the cookie by
`dispatch_request`). Missing or mismatched tokens must fail closed.
"""
import handlers as handlers_module
from handlers import _check_csrf, _csrf_field, _get_csrf_token
import tinyweb.handlers as handlers_module
from tinyweb.handlers import _check_csrf, _csrf_field, _get_csrf_token
def _set_token(token):

View file

@ -6,8 +6,8 @@ in sync via triggers, and the connection pool returning clean connections.
from unittest.mock import patch
from conftest import patch_dns_ok
import db as db_module
from db import get_db, return_db, index_url
import tinyweb.db as db_module
from tinyweb.db import get_db, return_db, index_url
def _mock_fetch_page(title="Test Page", body="test body text", links=None, meta=""):

View file

@ -3,7 +3,7 @@
`init_db` is called unconditionally on startup, so it must be idempotent
and create every table/trigger the rest of the app expects.
"""
from db import get_db, return_db, init_db, get_setting, set_setting, get_site_name
from tinyweb.db import get_db, return_db, init_db, get_setting, set_setting, get_site_name
EXPECTED_TABLES = {

View file

@ -6,7 +6,7 @@ could escape the quoting. These tests keep that regression dead.
"""
import pytest
from handlers import _sanitize_fts_query
from tinyweb.handlers import _sanitize_fts_query
def test_empty_query_returns_no_match_token():

View file

@ -8,8 +8,8 @@ import io
import pytest
import app as app_module
from gateway import GatewayHandler, MAX_BODY_SIZE
from tinyweb import app as app_module
from tinyweb.gateway import GatewayHandler, MAX_BODY_SIZE
class FakeHeaders:
@ -72,7 +72,7 @@ def test_post_at_size_cap_accepted():
rfile=io.BytesIO(b""),
)
# Stub out local_dispatch so _forward doesn't try the network path.
from gateway import GatewayState
from tinyweb.gateway import GatewayState
original = GatewayState.local_dispatch
GatewayState.local_dispatch = lambda data: {
"status": 404, "content_type": "text/plain", "body": "nope",

View file

@ -4,8 +4,8 @@ The bulk-delete confirmation flow is a data-loss guard added in commit
8dffd8c a stray POST without `confirmed=1` must render the confirmation
page instead of actually deleting.
"""
from db import get_db, return_db
from handlers import (
from tinyweb.db import get_db, return_db
from tinyweb.handlers import (
handle_bulk_action,
handle_edit_form,
handle_edit_submit,

View file

@ -1,5 +1,5 @@
"""Tests for `handle_search` — the home page + primary user flow."""
from handlers import handle_search
from tinyweb.handlers import handle_search
def test_empty_index_empty_query_shows_welcome(temp_db, csrf_session):

View file

@ -6,9 +6,9 @@ available and falls back to a live fetch otherwise.
"""
from unittest.mock import patch
import handlers as handlers_module
from db import get_db, return_db
from handlers import handle_subscription_add, handle_subscription_browse
import tinyweb.handlers as handlers_module
from tinyweb.db import get_db, return_db
from tinyweb.handlers import handle_subscription_add, handle_subscription_browse
VALID_HASH = "a" * 32

View file

@ -4,8 +4,8 @@ Tags are stored via a join table, so orphaned rows in `tags` can accumulate
if `_cleanup_orphaned_tags` isn't called after deletion/retagging. Tag
counts shown in the UI rely on this being right.
"""
from db import get_db, return_db
from handlers import (
from tinyweb.db import get_db, return_db
from tinyweb.handlers import (
_cleanup_orphaned_tags,
_get_page_tags,
_set_page_tags,

View file

@ -7,7 +7,7 @@ skip Wikipedia special pages, resolve relatives via urljoin.
from unittest.mock import patch
from conftest import patch_dns_ok
import db as db_module
import tinyweb.db as db_module
class FakeResponse:

View file

@ -1,5 +1,5 @@
"""Tests for `_paginate` and `_page_nav`."""
from handlers import _paginate, _page_nav, PER_PAGE
from tinyweb.handlers import _paginate, _page_nav, PER_PAGE
def test_paginate_default_is_one():

View file

@ -14,12 +14,12 @@ from unittest.mock import patch
import pytest
import app as app_module
import db as db_module
import handlers as handlers_module
from tinyweb import app as app_module
import tinyweb.db as db_module
import tinyweb.handlers as handlers_module
from conftest import patch_dns_fail, patch_dns_ok
from db import clean_url
from handlers import _sanitize_fts_query, handle_bulk_action
from tinyweb.db import clean_url
from tinyweb.handlers import _sanitize_fts_query, handle_bulk_action
def test_6ffd38d_clean_url_preserves_www_when_bare_domain_fails(monkeypatch):
@ -47,7 +47,7 @@ def test_1bc695f_fts_sanitizer_drops_operator_words(op):
def test_1bc695f_gateway_rejects_oversize_body():
"""1bc695f: 16 MiB body-size cap prevents memory-exhaustion DoS."""
from tests.test_gateway_limits import FakeGatewayHandler
from gateway import MAX_BODY_SIZE
from tinyweb.gateway import MAX_BODY_SIZE
h = FakeGatewayHandler(
path="/add", method="POST",
headers={"Content-Length": str(MAX_BODY_SIZE + 1)},
@ -70,7 +70,7 @@ def test_1bc695f_mesh_rejects_non_whitelisted_paths():
def test_1bc695f_pool_returns_clean_connection(temp_db, monkeypatch):
"""1bc695f: uncommitted transactions on a pooled connection used to leak
into the next consumer."""
from db import get_db, return_db
from tinyweb.db import get_db, return_db
db = get_db()
db.execute(
"INSERT INTO pages (url, title, body) VALUES (?, ?, ?)",
@ -88,7 +88,7 @@ def test_1bc695f_pool_returns_clean_connection(temp_db, monkeypatch):
def test_8dffd8c_bulk_delete_requires_confirmation(seeded_db, csrf_session):
"""8dffd8c: bulk delete without confirmed=1 must render a confirm page
instead of deleting the JS confirm on /pages is a first-line filter only."""
from db import get_db, return_db
from tinyweb.db import get_db, return_db
db = get_db()
try:
pid = db.execute("SELECT id FROM pages LIMIT 1").fetchone()["id"]

View file

@ -6,7 +6,7 @@ hiding pages the user meant to share — both are worth a regression net.
"""
import pytest
from handlers import _page_is_shared
from tinyweb.handlers import _page_is_shared
@pytest.mark.parametrize("mode", ["exclude_private", "require_public"])

View file

@ -9,7 +9,7 @@ from unittest.mock import patch
import pytest
from db import _validate_url_target
from tinyweb.db import _validate_url_target
def _mock_getaddrinfo(address):

View file

@ -6,7 +6,7 @@ this function can silently cause duplicate rows or mask legitimate saves.
import pytest
from conftest import patch_dns_ok, patch_dns_fail
from db import clean_url, TRACKING_PARAMS
from tinyweb.db import clean_url, TRACKING_PARAMS
def test_strips_fragment(monkeypatch):