Buildroot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [Buildroot] [PATCH] package/python-servestatic: new package
@ 2026-09-07  6:08 Sebastian Michel
  2026-10-03 17:34 ` Thomas Petazzoni via buildroot
  0 siblings, 1 reply; 2+ messages in thread
From: Sebastian Michel @ 2026-09-07  6:08 UTC (permalink / raw)
  To: buildroot; +Cc: James Hilliard, Thomas Petazzoni, Sebastian Michel

Added package for ServeStatic, version 4.3.2.
https://github.com/Archmonger/ServeStatic

Added a runtime test that verifies static-file responses through both
the WSGI and ASGI interfaces. Also verified that the installed
servestatic command-line entry point starts successfully.

Signed-off-by: Sebastian Michel <sebastian.michel@oss.othermo.de>
---
 DEVELOPERS                                    |   5 +
 package/Config.in                             |   1 +
 package/python-servestatic/Config.in          |  11 ++
 .../python-servestatic.hash                   |   3 +
 .../python-servestatic/python-servestatic.mk  |  13 ++
 .../package/sample_python_servestatic.py      | 118 ++++++++++++++++++
 .../tests/package/test_python_servestatic.py  |  19 +++
 7 files changed, 170 insertions(+)
 create mode 100644 package/python-servestatic/Config.in
 create mode 100644 package/python-servestatic/python-servestatic.hash
 create mode 100644 package/python-servestatic/python-servestatic.mk
 create mode 100644 support/testing/tests/package/sample_python_servestatic.py
 create mode 100644 support/testing/tests/package/test_python_servestatic.py

diff --git a/DEVELOPERS b/DEVELOPERS
index 53104cc60b..4e3dfb370c 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -3001,6 +3001,11 @@ F:	package/lrzip/
 N:	Sam Voss <sam.voss@gmail.com>
 F:	package/ripgrep/
 
+N:	Sebastian Michel <sebastian.michel@oss.othermo.de>
+F:	package/python-servestatic/
+F:	support/testing/tests/package/sample_python_servestatic.py
+F:	support/testing/tests/package/test_python_servestatic.py
+
 N:	Sébastien Szymanski <sebastien.szymanski@armadeus.com>
 F:	board/armadeus/
 F:	configs/armadeus*
diff --git a/package/Config.in b/package/Config.in
index 6058800682..79871aefd4 100644
--- a/package/Config.in
+++ b/package/Config.in
@@ -1418,6 +1418,7 @@ menu "External python modules"
 	source "package/python-sentry-sdk/Config.in"
 	source "package/python-serial/Config.in"
 	source "package/python-serial-asyncio/Config.in"
+	source "package/python-servestatic/Config.in"
 	source "package/python-service-identity/Config.in"
 	source "package/python-setproctitle/Config.in"
 	source "package/python-setuptools/Config.in"
diff --git a/package/python-servestatic/Config.in b/package/python-servestatic/Config.in
new file mode 100644
index 0000000000..b2a1e2a45d
--- /dev/null
+++ b/package/python-servestatic/Config.in
@@ -0,0 +1,11 @@
+config BR2_PACKAGE_PYTHON_SERVESTATIC
+	bool "python-servestatic"
+	select BR2_PACKAGE_PYTHON_ASGIREF # runtime dependency
+	select BR2_PACKAGE_PYTHON3_ZLIB # runtime dependency for cli / servestatic.storage usage
+	help
+	  Production-grade static file server for Python WSGI and ASGI.
+
+	  ServeStatic runs standalone or as middleware for an existing
+	  WSGI, ASGI, or Django application.
+
+	  https://github.com/Archmonger/ServeStatic
diff --git a/package/python-servestatic/python-servestatic.hash b/package/python-servestatic/python-servestatic.hash
new file mode 100644
index 0000000000..6d1fb6ecf9
--- /dev/null
+++ b/package/python-servestatic/python-servestatic.hash
@@ -0,0 +1,3 @@
+# Locally computed sha256 checksums
+sha256  1ab67d0654910c000864f9b6d7fefa8befc21a2156d539cf9e519afcc27c4d63  python-servestatic-4.3.2.tar.gz
+sha256  1770e2fc440cce75b2b2bd1580c6d59f58a497f0bb7abe91a0136bee0f688bf3  LICENSE.md
diff --git a/package/python-servestatic/python-servestatic.mk b/package/python-servestatic/python-servestatic.mk
new file mode 100644
index 0000000000..26d93fe9b5
--- /dev/null
+++ b/package/python-servestatic/python-servestatic.mk
@@ -0,0 +1,13 @@
+################################################################################
+#
+# python-servestatic
+#
+################################################################################
+
+PYTHON_SERVESTATIC_VERSION = 4.3.2
+PYTHON_SERVESTATIC_SITE = $(call github,Archmonger,ServeStatic,$(PYTHON_SERVESTATIC_VERSION))
+PYTHON_SERVESTATIC_SETUP_TYPE = hatch
+PYTHON_SERVESTATIC_LICENSE = MIT
+PYTHON_SERVESTATIC_LICENSE_FILES = LICENSE.md
+
+$(eval $(python-package))
diff --git a/support/testing/tests/package/sample_python_servestatic.py b/support/testing/tests/package/sample_python_servestatic.py
new file mode 100644
index 0000000000..22c69cdbcf
--- /dev/null
+++ b/support/testing/tests/package/sample_python_servestatic.py
@@ -0,0 +1,118 @@
+import asyncio
+import os
+import tempfile
+from collections.abc import Callable
+from types import TracebackType
+from typing import TypeAlias
+from wsgiref.types import StartResponse
+
+from asgiref.typing import HTTPScope, HTTPRequestEvent, ASGIVersions
+
+from servestatic import ServeStatic, ServeStaticASGI
+
+CONTENT = b"ServeStatic works on Buildroot.\n"
+
+_ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
+_OptExcInfo: TypeAlias = _ExcInfo | tuple[None, None, None]
+
+
+def assert_response_ok(status, headers, body):
+    assert status == 200
+    assert headers["content-length"] == str(len(CONTENT))
+    assert headers["content-type"].startswith("text/plain")
+    assert body == CONTENT
+
+
+def test_wsgi(content_directory):
+    app = ServeStatic(None, root=content_directory)
+    response_data = {}
+
+    class DummyStartResponse(StartResponse):
+        def __call__(self, status: str, headers: list[tuple[str, str]], exc_info: _OptExcInfo | None = None, /) -> \
+                Callable[[bytes], object]:
+            response_data["status"] = int(status.split()[0])
+            response_data["headers"] = {
+                key.lower(): value for key, value in headers
+            }
+            return super().__call__(status, headers, exc_info)
+
+    start_response = DummyStartResponse()
+
+    environ_test_request = {
+        "PATH_INFO": "/test_content.txt",
+        "REQUEST_METHOD": "GET",
+    }
+    response = app(environ_test_request, start_response)
+    try:
+        body = b"".join(response)
+    finally:
+        close = getattr(response, "close", None)
+        if callable(close):
+            close: Callable[None]
+            close()
+
+    assert_response_ok(
+        response_data["status"], response_data["headers"], body
+    )
+
+
+async def test_asgi(content_directory):
+    app = ServeStaticASGI(None, root=content_directory)
+    messages_received_from_application = []
+
+    async def receive() -> HTTPRequestEvent:
+        return HTTPRequestEvent(
+            type="http.request",
+            body=b"",
+            more_body=False,
+        )
+
+    # will be invoked when the application "sends" data back -> append all sent data to the messages list
+    async def send(message):
+        messages_received_from_application.append(message)
+
+    # run the test request
+    scope_test_request = HTTPScope(
+        type="http",
+        asgi=ASGIVersions(
+            version="3.0",
+            spec_version="2.5"
+        ),
+        http_version="1.1",
+        method="GET",
+        scheme="http",
+        path="/test_content.txt",
+        raw_path=b"/hello.txt",
+        query_string=b"",
+        root_path="",
+        headers=[],
+        client=("127.0.0.1", 12345),
+        server=("127.0.0.1", 80),
+        extensions=None
+    )
+    await app(scope_test_request, receive, send)
+
+    # first segment contains the headers
+    message_with_headers = messages_received_from_application[0]
+
+    # convert headers into correct form for assert_response_ok
+    headers = {
+        key.decode(): value.decode() for key, value in message_with_headers["headers"]
+    }
+
+    # collect content of all "body" type messages
+    body = b"".join(
+        message.get("body", b"")
+        for message in messages_received_from_application
+        if message["type"] == "http.response.body"
+    )
+
+    # assert everything works as expected
+    assert_response_ok(message_with_headers["status"], headers, body)
+
+
+with tempfile.TemporaryDirectory() as tmp_content_directory:
+    with open(os.path.join(tmp_content_directory, "test_content.txt"), "wb") as static_file:
+        static_file.write(CONTENT)
+    test_wsgi(tmp_content_directory)
+    asyncio.run(test_asgi(tmp_content_directory))
diff --git a/support/testing/tests/package/test_python_servestatic.py b/support/testing/tests/package/test_python_servestatic.py
new file mode 100644
index 0000000000..d9c762962a
--- /dev/null
+++ b/support/testing/tests/package/test_python_servestatic.py
@@ -0,0 +1,19 @@
+from tests.package.test_python import TestPythonPackageBase
+
+
+class TestPythonServeStatic(TestPythonPackageBase):
+    __test__ = True
+    config = TestPythonPackageBase.config + \
+        """
+        BR2_PACKAGE_PYTHON3=y
+        BR2_PACKAGE_PYTHON_SERVESTATIC=y
+        """
+
+    sample_scripts = ["tests/package/sample_python_servestatic.py"]
+
+    def run_sample_scripts(self):
+        self.assertRunOk(
+            "servestatic --help | grep -q 'Process static files'"
+        )
+
+        super().run_sample_scripts()
-- 
2.55.0

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [Buildroot] [PATCH] package/python-servestatic: new package
  2026-09-07  6:08 [Buildroot] [PATCH] package/python-servestatic: new package Sebastian Michel
@ 2026-10-03 17:34 ` Thomas Petazzoni via buildroot
  0 siblings, 0 replies; 2+ messages in thread
From: Thomas Petazzoni via buildroot @ 2026-10-03 17:34 UTC (permalink / raw)
  To: Sebastian Michel; +Cc: buildroot, James Hilliard

On Mon, Sep 07, 2026 at 08:08:10AM +0200, Sebastian Michel wrote:
> Added package for ServeStatic, version 4.3.2.
> https://github.com/Archmonger/ServeStatic
> 
> Added a runtime test that verifies static-file responses through both
> the WSGI and ASGI interfaces. Also verified that the installed
> servestatic command-line entry point starts successfully.
> 
> Signed-off-by: Sebastian Michel <sebastian.michel@oss.othermo.de>

Applied to master, thanks!

Best regards,

Thomas Petazzoni
-- 
Thomas Petazzoni, co-owner and CEO, Bootlin
Embedded Linux and Kernel engineering and training
https://bootlin.com
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-03 17:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07  6:08 [Buildroot] [PATCH] package/python-servestatic: new package Sebastian Michel
2026-10-03 17:34 ` Thomas Petazzoni via buildroot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox