Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pass import path instead of callable in 'screenshot_apps' #30

Merged
merged 1 commit into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ jobs:
run: playwright install
- name: Run unittest
run: pytest
- name: Upload failed image artifact
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-images-${{ matrix.os }}-${{ matrix.python-version }}
# https://docs.pytest.org/en/stable/how-to/tmp_path.html#temporary-directory-location-and-retention
path: /tmp/pytest-of-*/pytest-*/*/*/*.obtained.png
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,28 @@ It also generates a PDF file when `pdf` option is given, which might be useful w
## Local WSGI application

`sphinxcontrib-screenshot` can launch your local WSGI applications and take screenshot of thems.
Define them in the `screenshot_apps` dict. The key must be a name you choose for your applications, and the value must be a callable that creates a WSGI app:
Define them in the `screenshot_apps` dict.
The key must be a name you choose for your applications, and the value must be a callable that creates a WSGI app:

```python
from flask import Flask

def create_app():
def create_app(sphinx_app):
app = Flask(__name__)

@app.route("/hello")
def hello():
return "Hello, world!"

return app
```


```
screenshot_apps = {
"example": create_app,
"example": "my_module.my_app:create_app",
}
```
Note that you might manually add your application module in `sys.path`.

Then you can use a Sphinx substitution with your application name to refer to its temporary URL:

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dev =
mypy
Pillow
pytest
pytest-regressions
pytest-regressions[image]
sphinx[test]
toml
twine
Expand Down
17 changes: 13 additions & 4 deletions sphinxcontrib/screenshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

import hashlib
import importlib
import os
import threading
import typing
Expand Down Expand Up @@ -208,20 +209,28 @@ def run(self) -> typing.List[nodes.Node]:
app_threads = {}


def resolve_wsgi_app_import(app: Sphinx, app_path: str):
module_path, app_attribute = app_path.split(":")
module = importlib.import_module(module_path)
app_attr = getattr(module, app_attribute)
wsgi_app = app_attr(app) if callable(app_attr) else app_attr
return wsgi_app


def setup_apps(app: Sphinx, config: Config):
"""Start the WSGI application threads.

A new replacement is created for each WSGI app."""
for app_name, app_builder in config.screenshot_apps.items():
for wsgi_app_name, wsgi_app_path in config.screenshot_apps.items():
port = pick_unused_port()
config.rst_prolog = (
config.rst_prolog or
"") + f".. |{app_name}| replace:: http://localhost:{port}\n"
wsgi_app = app_builder()
"") + f"\n.. |{wsgi_app_name}| replace:: http://localhost:{port}\n"
wsgi_app = resolve_wsgi_app_import(app, wsgi_app_path)
httpd = wsgiref.simple_server.make_server("localhost", port, wsgi_app)
thread = threading.Thread(target=httpd.serve_forever)
thread.start()
app_threads[app_name] = (httpd, thread)
app_threads[wsgi_app_name] = (httpd, thread)


def teardown_apps(app: Sphinx, exception: typing.Optional[Exception]):
Expand Down
Empty file.
9 changes: 4 additions & 5 deletions tests/roots/test-wsgi-apps/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def hello_world_app(environ, start_response):
headers = [('Content-type', 'text/plain; charset=utf-8')]
start_response('200 OK', headers)
return [b'Hello, World!']
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent.resolve()))

extensions = ['sphinxcontrib.screenshot']
screenshot_apps = {"example": lambda: hello_world_app}
screenshot_apps = {"example": "example_app:create_app"}
25 changes: 25 additions & 0 deletions tests/roots/test-wsgi-apps/example_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def create_app(sphinx_app):

def hello_world_app(environ, start_response):
headers = [('Content-type', 'text/html; charset=utf-8')]
start_response('200 OK', headers)
style = b"font-family: arial; font-size: 10px; margin: 0; padding: 0"
return [
b"<html>" + b'<body style="' + style + b'">' + b"Hello, World!" +
b"</body>" + b"</html>"
]

return hello_world_app
2 changes: 2 additions & 0 deletions tests/roots/test-wsgi-apps/index.rst
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.. screenshot:: |example|
:width: 800
:height: 600
4 changes: 2 additions & 2 deletions tests/test_wsgi_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

@pytest.mark.sphinx('html', testroot="wsgi-apps")
def test_default(app: SphinxTestApp, status: StringIO, warning: StringIO,
data_regression) -> None:
image_regression) -> None:
app.build()
out_html = app.outdir / "index.html"

Expand All @@ -29,4 +29,4 @@ def test_default(app: SphinxTestApp, status: StringIO, warning: StringIO,

img_path = app.outdir / imgs[0]['src']
with open(img_path, "rb") as fd:
data_regression.check(fd.read())
image_regression.check(fd.read())
Binary file added tests/test_wsgi_apps/test_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 0 additions & 122 deletions tests/test_wsgi_apps/test_default.yml

This file was deleted.

Loading