From db4f036729187c0f31165c010dc25f03d1ffb6e3 Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Thu, 4 Jun 2026 06:22:30 -0700 Subject: [PATCH] chore: scaffold tools/ package + stdlib http client Per sub-project-2 plan Task 1. Adds tools/ to setuptools packages, gitignores media/, and a minimal injectable urllib HTTP client. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 1 + pyproject.toml | 6 +++++- tests/test_tools_smoke.py | 5 +++++ tools/__init__.py | 0 tools/http.py | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/test_tools_smoke.py create mode 100644 tools/__init__.py create mode 100644 tools/http.py diff --git a/.gitignore b/.gitignore index 692f12e..287ef9d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__/ *.pyc .pytest_cache/ .venv/ +media/ diff --git a/pyproject.toml b/pyproject.toml index 2b17d18..96b5411 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,4 +12,8 @@ requires = ["setuptools>=68"] build-backend = "setuptools.build_meta" [tool.setuptools] -packages = ["hef"] +packages = ["hef", "tools"] + +[project.scripts] +hef-ingest = "tools.ingest_cli:main" +hef-review = "tools.review_cli:main" diff --git a/tests/test_tools_smoke.py b/tests/test_tools_smoke.py new file mode 100644 index 0000000..b7be4af --- /dev/null +++ b/tests/test_tools_smoke.py @@ -0,0 +1,5 @@ +def test_tools_package_imports(): + import tools + import tools.http + + assert tools is not None diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/http.py b/tools/http.py new file mode 100644 index 0000000..18ddbfb --- /dev/null +++ b/tools/http.py @@ -0,0 +1,33 @@ +"""Minimal HTTP client over urllib: timeout, small retry, user-agent. Injectable.""" + +from __future__ import annotations + +import json as _json +import time +import urllib.request + +USER_AGENT = "human-experience-filter-ingest/0.1 (+local art installation)" + + +class HttpClient: + def __init__(self, *, timeout=30.0, retries=2, opener=None): + self.timeout, self.retries = timeout, retries + self._opener = opener or urllib.request.urlopen + + def get_bytes(self, url, *, headers=None): + last = None + for attempt in range(self.retries + 1): + try: + req = urllib.request.Request( + url, headers={"User-Agent": USER_AGENT, **(headers or {})} + ) + with self._opener(req, timeout=self.timeout) as resp: + return resp.read() + except Exception as exc: # noqa: BLE001 - retried below + last = exc + if attempt < self.retries: + time.sleep(0.2 * (attempt + 1)) + raise last + + def get_json(self, url, *, headers=None): + return _json.loads(self.get_bytes(url, headers=headers).decode("utf-8"))