sphinxcontrib-screenshot 0.1.3__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sphinxcontrib-screenshot might be problematic. Click here for more details.
- {sphinxcontrib_screenshot-0.1.3/sphinxcontrib_screenshot.egg-info → sphinxcontrib_screenshot-0.2.0}/PKG-INFO +23 -62
- sphinxcontrib_screenshot-0.2.0/README.md +33 -0
- sphinxcontrib_screenshot-0.2.0/pyproject.toml +109 -0
- sphinxcontrib_screenshot-0.2.0/setup.cfg +4 -0
- sphinxcontrib_screenshot-0.2.0/sphinxcontrib/screenshot.py +325 -0
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0/sphinxcontrib_screenshot.egg-info}/PKG-INFO +23 -62
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0}/sphinxcontrib_screenshot.egg-info/SOURCES.txt +9 -3
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0}/sphinxcontrib_screenshot.egg-info/requires.txt +12 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_browsers.py +49 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_color_scheme.py +48 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_contexts.py +32 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_full_page.py +46 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_headers.py +33 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_pdf.py +34 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_root.py +61 -0
- sphinxcontrib_screenshot-0.2.0/tests/test_wsgi_apps.py +32 -0
- sphinxcontrib_screenshot-0.1.3/README.md +0 -84
- sphinxcontrib_screenshot-0.1.3/setup.cfg +0 -60
- sphinxcontrib_screenshot-0.1.3/setup.py +0 -17
- sphinxcontrib_screenshot-0.1.3/sphinxcontrib/screenshot.py +0 -205
- sphinxcontrib_screenshot-0.1.3/tests/test_it.py +0 -70
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0}/LICENSE +0 -0
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0}/sphinxcontrib_screenshot.egg-info/dependency_links.txt +0 -0
- {sphinxcontrib_screenshot-0.1.3 → sphinxcontrib_screenshot-0.2.0}/sphinxcontrib_screenshot.egg-info/top_level.txt +0 -0
|
@@ -1,103 +1,64 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: sphinxcontrib-screenshot
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A
|
|
5
|
-
|
|
6
|
-
Author: Shuhei Iitsuka
|
|
7
|
-
Author-email: tushuhei@gmail.com
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Sphinx extension to embed webpage screenshots.
|
|
5
|
+
Author-email: Shuhei Iitsuka <tushuhei@gmail.com>
|
|
8
6
|
License: Apache-2.0
|
|
7
|
+
Project-URL: repository, https://github.com/tushuhei/sphinxcontrib-screenshot/
|
|
9
8
|
Classifier: Development Status :: 3 - Alpha
|
|
10
9
|
Classifier: Operating System :: OS Independent
|
|
11
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
16
|
Requires-Python: >=3.9
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
License-File: LICENSE
|
|
18
19
|
Requires-Dist: playwright
|
|
19
20
|
Requires-Dist: sphinx
|
|
21
|
+
Requires-Dist: portpicker
|
|
20
22
|
Provides-Extra: dev
|
|
21
23
|
Requires-Dist: beautifulsoup4; extra == "dev"
|
|
22
24
|
Requires-Dist: build; extra == "dev"
|
|
23
25
|
Requires-Dist: flake8; extra == "dev"
|
|
26
|
+
Requires-Dist: flake8-pyproject; extra == "dev"
|
|
24
27
|
Requires-Dist: isort; extra == "dev"
|
|
25
28
|
Requires-Dist: mypy; extra == "dev"
|
|
26
29
|
Requires-Dist: Pillow; extra == "dev"
|
|
30
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
27
31
|
Requires-Dist: pytest; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-regressions[image]; extra == "dev"
|
|
28
33
|
Requires-Dist: sphinx[test]; extra == "dev"
|
|
29
34
|
Requires-Dist: toml; extra == "dev"
|
|
35
|
+
Requires-Dist: tox; extra == "dev"
|
|
30
36
|
Requires-Dist: twine; extra == "dev"
|
|
31
37
|
Requires-Dist: types-beautifulsoup4; extra == "dev"
|
|
32
38
|
Requires-Dist: types-docutils; extra == "dev"
|
|
39
|
+
Requires-Dist: types-portpicker; extra == "dev"
|
|
33
40
|
Requires-Dist: types-Pillow; extra == "dev"
|
|
34
41
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
42
|
+
Requires-Dist: user-agents; extra == "dev"
|
|
35
43
|
Requires-Dist: yapf; extra == "dev"
|
|
44
|
+
Provides-Extra: doc
|
|
45
|
+
Requires-Dist: myst-parser; extra == "doc"
|
|
46
|
+
Requires-Dist: shibuya; extra == "doc"
|
|
47
|
+
Requires-Dist: sphinx; extra == "doc"
|
|
36
48
|
|
|
37
49
|
# sphinxcontrib-screenshot
|
|
38
50
|
|
|
39
51
|
A Sphinx extension to embed website screenshots.
|
|
40
52
|
|
|
41
|
-

|
|
42
|
-
|
|
43
|
-
## Install
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
pip install sphinxcontrib-screenshot
|
|
47
|
-
playwright install
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Usage
|
|
51
|
-
|
|
52
|
-
Add `sphinxcontrib.screenshot` to your `conf.py`.
|
|
53
|
-
|
|
54
|
-
```py
|
|
55
|
-
extensions = ["sphinxcontrib.screenshot"]
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Then use the `screenshot` directive in your Sphinx source file.
|
|
59
|
-
|
|
60
|
-
```rst
|
|
61
|
-
.. screenshot:: http://www.example.com
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
You can also specify the screen size for the screenshot with `width` and `height` parameters.
|
|
65
|
-
|
|
66
|
-
```rst
|
|
67
|
-
.. screenshot:: http://www.example.com
|
|
68
|
-
:width: 1280
|
|
69
|
-
:height: 960
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
You can include a caption for the screenshot's `figure` directive.
|
|
73
|
-
|
|
74
53
|
```rst
|
|
75
54
|
.. screenshot:: http://www.example.com
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```rst
|
|
82
|
-
.. screenshot:: http://www.example.com
|
|
83
|
-
|
|
84
|
-
document.querySelector('button').click();
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Pro tips
|
|
88
|
-
`sphinxcontrib-screenshot` supports URLs with the HTTP and HTTPS protocols.
|
|
89
|
-
To take screenshots of local files and build the document while running a local server for them, you can use the NPM library [concurrently](https://www.npmjs.com/package/concurrently) in the following way:
|
|
90
|
-
|
|
91
|
-
### Build the document
|
|
92
|
-
```bash
|
|
93
|
-
npx --yes concurrently -k --success=first "make html" "python3 -m http.server 3000 --directory=examples"
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Watch and build the document
|
|
97
|
-
```bash
|
|
98
|
-
npx --yes concurrently -k "make livehtml" "python3 -m http.server 3000 --directory=examples"
|
|
55
|
+
:browser: chromium
|
|
56
|
+
:viewport-width: 1280
|
|
57
|
+
:viewport-height: 960
|
|
58
|
+
:color-scheme: dark
|
|
99
59
|
```
|
|
100
60
|
|
|
61
|
+
Read more in the [documentation](https://sphinxcontrib-screenshot.readthedocs.io).
|
|
101
62
|
|
|
102
63
|
## Notes
|
|
103
64
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# sphinxcontrib-screenshot
|
|
2
|
+
|
|
3
|
+
A Sphinx extension to embed website screenshots.
|
|
4
|
+
|
|
5
|
+
```rst
|
|
6
|
+
.. screenshot:: http://www.example.com
|
|
7
|
+
:browser: chromium
|
|
8
|
+
:viewport-width: 1280
|
|
9
|
+
:viewport-height: 960
|
|
10
|
+
:color-scheme: dark
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Read more in the [documentation](https://sphinxcontrib-screenshot.readthedocs.io).
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
|
|
17
|
+
This extension uses [Playwright](https://playwright.dev) to capture a screenshot of the specified website only.
|
|
18
|
+
No data is sent to any other external server; the request is limited to the specified website.
|
|
19
|
+
Be cautious: avoid including sensitive information (such as authentication data) in the directive content.
|
|
20
|
+
|
|
21
|
+
## Contributing
|
|
22
|
+
|
|
23
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details.
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
Apache 2.0; see [`LICENSE`](LICENSE) for details.
|
|
28
|
+
|
|
29
|
+
## Disclaimer
|
|
30
|
+
|
|
31
|
+
This project is not an official Google project. It is not supported by
|
|
32
|
+
Google and Google specifically disclaims all warranties as to its quality,
|
|
33
|
+
merchantability, or fitness for a particular purpose.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sphinxcontrib-screenshot"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A Sphinx extension to embed webpage screenshots."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Shuhei Iitsuka", email = "tushuhei@gmail.com" }
|
|
13
|
+
]
|
|
14
|
+
urls = { repository = "https://github.com/tushuhei/sphinxcontrib-screenshot/" }
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"License :: OSI Approved :: Apache Software License",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.9"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"playwright",
|
|
28
|
+
"sphinx",
|
|
29
|
+
"portpicker",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = [
|
|
34
|
+
"beautifulsoup4",
|
|
35
|
+
"build",
|
|
36
|
+
"flake8",
|
|
37
|
+
"flake8-pyproject",
|
|
38
|
+
"isort",
|
|
39
|
+
"mypy",
|
|
40
|
+
"Pillow",
|
|
41
|
+
"pre-commit",
|
|
42
|
+
"pytest",
|
|
43
|
+
"pytest-regressions[image]",
|
|
44
|
+
"sphinx[test]",
|
|
45
|
+
"toml",
|
|
46
|
+
"tox",
|
|
47
|
+
"twine",
|
|
48
|
+
"types-beautifulsoup4",
|
|
49
|
+
"types-docutils",
|
|
50
|
+
"types-portpicker",
|
|
51
|
+
"types-Pillow",
|
|
52
|
+
"types-setuptools",
|
|
53
|
+
"user-agents",
|
|
54
|
+
"yapf",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
doc = [
|
|
58
|
+
"myst-parser",
|
|
59
|
+
"shibuya",
|
|
60
|
+
"sphinx",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.setuptools]
|
|
64
|
+
packages = ["sphinxcontrib"]
|
|
65
|
+
include-package-data = true
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.package-data]
|
|
68
|
+
"sphinxcontrib" = ["*"]
|
|
69
|
+
|
|
70
|
+
[tool.yapf]
|
|
71
|
+
based_on_style = "yapf"
|
|
72
|
+
|
|
73
|
+
[tool.flake8]
|
|
74
|
+
indent-size = 2
|
|
75
|
+
|
|
76
|
+
[tool.mypy]
|
|
77
|
+
python_version = 3.9
|
|
78
|
+
pretty = true
|
|
79
|
+
packages = ["sphinxcontrib"]
|
|
80
|
+
|
|
81
|
+
[tool.tox]
|
|
82
|
+
requires = ["tox>=4.19"]
|
|
83
|
+
env_list = [
|
|
84
|
+
"py39",
|
|
85
|
+
"py310",
|
|
86
|
+
"py311",
|
|
87
|
+
"py312",
|
|
88
|
+
"py313",
|
|
89
|
+
"style",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.tox.env_run_base]
|
|
93
|
+
deps = ["-e .[dev]"]
|
|
94
|
+
commands = [
|
|
95
|
+
["pytest", "--showlocals", "--full-trace", "{posargs}"],
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
[tool.tox.env.style]
|
|
99
|
+
skip_install = true
|
|
100
|
+
commands = [
|
|
101
|
+
["pre-commit", "run", "--all-files", "--show-diff-on-failure"],
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[tool.tox.env.doc]
|
|
105
|
+
skip_install = true
|
|
106
|
+
deps = ["-e .[doc]"]
|
|
107
|
+
commands = [
|
|
108
|
+
["sphinx-build", "--builder", "html", "doc", "build/sphinx/html"],
|
|
109
|
+
]
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Copyright 2023 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import importlib
|
|
17
|
+
import importlib.metadata
|
|
18
|
+
import os
|
|
19
|
+
import threading
|
|
20
|
+
import typing
|
|
21
|
+
import wsgiref.simple_server
|
|
22
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
23
|
+
from urllib.parse import urlparse
|
|
24
|
+
|
|
25
|
+
from docutils import nodes
|
|
26
|
+
from docutils.parsers.rst import directives
|
|
27
|
+
from docutils.parsers.rst.directives.images import Figure
|
|
28
|
+
from playwright._impl._helper import ColorScheme
|
|
29
|
+
from playwright.sync_api import Browser, BrowserContext
|
|
30
|
+
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
|
|
31
|
+
from playwright.sync_api import sync_playwright
|
|
32
|
+
from portpicker import pick_unused_port
|
|
33
|
+
from sphinx.application import Sphinx
|
|
34
|
+
from sphinx.config import Config
|
|
35
|
+
from sphinx.util.docutils import SphinxDirective
|
|
36
|
+
|
|
37
|
+
Meta = typing.TypedDict('Meta', {
|
|
38
|
+
'version': str,
|
|
39
|
+
'parallel_read_safe': bool,
|
|
40
|
+
'parallel_write_safe': bool
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ScreenshotDirective(SphinxDirective, Figure):
|
|
45
|
+
"""Sphinx Screenshot Dirctive.
|
|
46
|
+
|
|
47
|
+
This directive embeds a screenshot of a webpage.
|
|
48
|
+
|
|
49
|
+
# Example
|
|
50
|
+
|
|
51
|
+
You can simply pass a URL for a webpage that you want to take a screenshot.
|
|
52
|
+
|
|
53
|
+
```rst
|
|
54
|
+
.. screenshot:: http://www.example.com
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You can also specify the screen size for the screenshot with
|
|
58
|
+
`viewport-width` and `viewport-height` parameters in pixel.
|
|
59
|
+
|
|
60
|
+
```rst
|
|
61
|
+
.. screenshot:: http://www.example.com
|
|
62
|
+
:viewport-width: 1280
|
|
63
|
+
:viewport-height: 960
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
You can describe the interaction that you want to have with the webpage
|
|
67
|
+
before taking a screenshot in JavaScript.
|
|
68
|
+
|
|
69
|
+
```rst
|
|
70
|
+
.. screenshot:: http://www.example.com
|
|
71
|
+
|
|
72
|
+
document.querySelector('button').click();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
It also generates a PDF file when `pdf` option is given, which might be
|
|
76
|
+
useful when you need scalable image assets.
|
|
77
|
+
|
|
78
|
+
```rst
|
|
79
|
+
.. screenshot:: http://www.example.com
|
|
80
|
+
:pdf:
|
|
81
|
+
```
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
required_arguments = 1 # URL
|
|
85
|
+
option_spec = {
|
|
86
|
+
**Figure.option_spec,
|
|
87
|
+
'browser': str,
|
|
88
|
+
'viewport-height': directives.positive_int,
|
|
89
|
+
'viewport-width': directives.positive_int,
|
|
90
|
+
'interactions': str,
|
|
91
|
+
'pdf': directives.flag,
|
|
92
|
+
'color-scheme': str,
|
|
93
|
+
'full-page': directives.flag,
|
|
94
|
+
'context': str,
|
|
95
|
+
'headers': directives.unchanged,
|
|
96
|
+
}
|
|
97
|
+
pool = ThreadPoolExecutor()
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def take_screenshot(
|
|
101
|
+
url: str, browser_name: str, viewport_width: int, viewport_height: int,
|
|
102
|
+
filepath: str, init_script: str, interactions: str, generate_pdf: bool,
|
|
103
|
+
color_scheme: ColorScheme, full_page: bool,
|
|
104
|
+
context_builder: typing.Optional[typing.Callable[[Browser, str, str],
|
|
105
|
+
BrowserContext]],
|
|
106
|
+
headers: dict):
|
|
107
|
+
"""Takes a screenshot with Playwright's Chromium browser.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
url (str): The HTTP/HTTPS URL of the webpage to screenshot.
|
|
111
|
+
viewport_width (int): The width of the screenshot in pixels.
|
|
112
|
+
viewport_height (int): The height of the screenshot in pixels.
|
|
113
|
+
filepath (str): The path to save the screenshot to.
|
|
114
|
+
init_script (str): JavaScript code to be evaluated after the document
|
|
115
|
+
was created but before any of its scripts were run. See more details at
|
|
116
|
+
https://playwright.dev/python/docs/api/class-page#page-add-init-script
|
|
117
|
+
interactions (str): JavaScript code to run before taking the screenshot
|
|
118
|
+
after the page was loaded.
|
|
119
|
+
generate_pdf (bool): Generate a PDF file along with the screenshot.
|
|
120
|
+
color_scheme (str): The preferred color scheme. Can be 'light' or 'dark'.
|
|
121
|
+
context: A method to build the Playwright context.
|
|
122
|
+
"""
|
|
123
|
+
with sync_playwright() as playwright:
|
|
124
|
+
browser = getattr(playwright, browser_name).launch()
|
|
125
|
+
|
|
126
|
+
if context_builder:
|
|
127
|
+
try:
|
|
128
|
+
context = context_builder(browser, url, color_scheme)
|
|
129
|
+
except PlaywrightTimeoutError:
|
|
130
|
+
raise RuntimeError(
|
|
131
|
+
'Timeout error occured at %s in executing py init script %s' %
|
|
132
|
+
(url, context_builder.__name__))
|
|
133
|
+
else:
|
|
134
|
+
context = browser.new_context(color_scheme=color_scheme)
|
|
135
|
+
|
|
136
|
+
page = context.new_page()
|
|
137
|
+
page.set_default_timeout(10000)
|
|
138
|
+
page.set_viewport_size({
|
|
139
|
+
'width': viewport_width,
|
|
140
|
+
'height': viewport_height
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
if init_script:
|
|
145
|
+
page.add_init_script(init_script)
|
|
146
|
+
page.set_extra_http_headers(headers)
|
|
147
|
+
page.goto(url)
|
|
148
|
+
page.wait_for_load_state('networkidle')
|
|
149
|
+
|
|
150
|
+
# Execute interactions
|
|
151
|
+
if interactions:
|
|
152
|
+
page.evaluate(interactions)
|
|
153
|
+
page.wait_for_load_state('networkidle')
|
|
154
|
+
except PlaywrightTimeoutError:
|
|
155
|
+
raise RuntimeError('Timeout error occured at %s in executing\n%s' %
|
|
156
|
+
(url, interactions))
|
|
157
|
+
page.screenshot(path=filepath, full_page=full_page)
|
|
158
|
+
if generate_pdf:
|
|
159
|
+
page.emulate_media(media='screen')
|
|
160
|
+
root, ext = os.path.splitext(filepath)
|
|
161
|
+
page.pdf(
|
|
162
|
+
width=f'{viewport_width}px',
|
|
163
|
+
height=f'{viewport_height}px',
|
|
164
|
+
path=root + '.pdf')
|
|
165
|
+
page.close()
|
|
166
|
+
browser.close()
|
|
167
|
+
|
|
168
|
+
def evaluate_substitutions(self, text: str) -> str:
|
|
169
|
+
substitutions = self.state.document.substitution_defs
|
|
170
|
+
for key, value in substitutions.items():
|
|
171
|
+
text = text.replace(f"|{key}|", value.astext())
|
|
172
|
+
return text
|
|
173
|
+
|
|
174
|
+
def run(self) -> typing.Sequence[nodes.Node]:
|
|
175
|
+
screenshot_init_script: str = self.env.config.screenshot_init_script or ''
|
|
176
|
+
|
|
177
|
+
# Ensure the screenshots directory exists
|
|
178
|
+
ss_dirpath = os.path.join(self.env.app.outdir, '_static', 'screenshots')
|
|
179
|
+
os.makedirs(ss_dirpath, exist_ok=True)
|
|
180
|
+
|
|
181
|
+
# Parse parameters
|
|
182
|
+
raw_url = self.arguments[0]
|
|
183
|
+
url = self.evaluate_substitutions(raw_url)
|
|
184
|
+
interactions = self.options.get('interactions', '')
|
|
185
|
+
browser = self.options.get('browser',
|
|
186
|
+
self.env.config.screenshot_default_browser)
|
|
187
|
+
viewport_height = self.options.get(
|
|
188
|
+
'viewport-height', self.env.config.screenshot_default_viewport_height)
|
|
189
|
+
viewport_width = self.options.get(
|
|
190
|
+
'viewport-width', self.env.config.screenshot_default_viewport_width)
|
|
191
|
+
color_scheme = self.options.get(
|
|
192
|
+
'color-scheme', self.env.config.screenshot_default_color_scheme)
|
|
193
|
+
pdf = 'pdf' in self.options
|
|
194
|
+
full_page = ('full-page' in self.options or
|
|
195
|
+
self.env.config.screenshot_default_full_page)
|
|
196
|
+
context = self.options.get('context', '')
|
|
197
|
+
headers = self.options.get('headers', '')
|
|
198
|
+
|
|
199
|
+
request_headers = {**self.env.config.screenshot_default_headers}
|
|
200
|
+
if headers:
|
|
201
|
+
for header in headers.strip().split("\n"):
|
|
202
|
+
name, value = header.split(" ", 1)
|
|
203
|
+
request_headers[name] = value
|
|
204
|
+
|
|
205
|
+
if urlparse(url).scheme not in {'http', 'https'}:
|
|
206
|
+
raise RuntimeError(
|
|
207
|
+
f'Invalid URL: {url}. Only HTTP/HTTPS URLs are supported.')
|
|
208
|
+
|
|
209
|
+
# Generate filename based on hash of parameters
|
|
210
|
+
hash_input = "_".join([
|
|
211
|
+
raw_url, browser,
|
|
212
|
+
str(viewport_height),
|
|
213
|
+
str(viewport_width), color_scheme, context, interactions,
|
|
214
|
+
str(full_page)
|
|
215
|
+
])
|
|
216
|
+
filename = hashlib.md5(hash_input.encode()).hexdigest() + '.png'
|
|
217
|
+
filepath = os.path.join(ss_dirpath, filename)
|
|
218
|
+
|
|
219
|
+
if context:
|
|
220
|
+
context_builder_path = self.config.screenshot_contexts[context]
|
|
221
|
+
context_builder = resolve_python_method(context_builder_path)
|
|
222
|
+
else:
|
|
223
|
+
context_builder = None
|
|
224
|
+
|
|
225
|
+
# Check if the file already exists. If not, take a screenshot
|
|
226
|
+
if not os.path.exists(filepath):
|
|
227
|
+
fut = self.pool.submit(ScreenshotDirective.take_screenshot, url, browser,
|
|
228
|
+
viewport_width, viewport_height, filepath,
|
|
229
|
+
screenshot_init_script, interactions, pdf,
|
|
230
|
+
color_scheme, full_page, context_builder,
|
|
231
|
+
request_headers)
|
|
232
|
+
fut.result()
|
|
233
|
+
|
|
234
|
+
# Create image and figure nodes
|
|
235
|
+
docdir = os.path.dirname(self.env.doc2path(self.env.docname))
|
|
236
|
+
rel_ss_dirpath = os.path.relpath(ss_dirpath, start=docdir)
|
|
237
|
+
rel_filepath = os.path.join(rel_ss_dirpath, filename).replace(os.sep, '/')
|
|
238
|
+
|
|
239
|
+
self.arguments[0] = rel_filepath
|
|
240
|
+
return super().run()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
app_threads = {}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def resolve_python_method(import_path: str):
|
|
247
|
+
module_path, method_name = import_path.split(":")
|
|
248
|
+
module = importlib.import_module(module_path)
|
|
249
|
+
method = getattr(module, method_name)
|
|
250
|
+
return method
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def setup_apps(app: Sphinx, config: Config):
|
|
254
|
+
"""Start the WSGI application threads.
|
|
255
|
+
|
|
256
|
+
A new replacement is created for each WSGI app."""
|
|
257
|
+
for wsgi_app_name, wsgi_app_path in config.screenshot_apps.items():
|
|
258
|
+
port = pick_unused_port()
|
|
259
|
+
config.rst_prolog = (
|
|
260
|
+
config.rst_prolog or
|
|
261
|
+
"") + f"\n.. |{wsgi_app_name}| replace:: http://localhost:{port}\n"
|
|
262
|
+
app_builder = resolve_python_method(wsgi_app_path)
|
|
263
|
+
wsgi_app = app_builder(app)
|
|
264
|
+
httpd = wsgiref.simple_server.make_server("localhost", port, wsgi_app)
|
|
265
|
+
thread = threading.Thread(target=httpd.serve_forever)
|
|
266
|
+
thread.start()
|
|
267
|
+
app_threads[wsgi_app_name] = (httpd, thread)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def teardown_apps(app: Sphinx, exception: typing.Optional[Exception]):
|
|
271
|
+
"""Shut down the WSGI application threads."""
|
|
272
|
+
for httpd, thread in app_threads.values():
|
|
273
|
+
httpd.shutdown()
|
|
274
|
+
thread.join()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def setup(app: Sphinx) -> Meta:
|
|
278
|
+
app.add_directive('screenshot', ScreenshotDirective)
|
|
279
|
+
app.add_config_value('screenshot_init_script', '', 'env')
|
|
280
|
+
app.add_config_value(
|
|
281
|
+
'screenshot_default_viewport_width',
|
|
282
|
+
1280,
|
|
283
|
+
'env',
|
|
284
|
+
description="The default width for screenshots")
|
|
285
|
+
app.add_config_value(
|
|
286
|
+
'screenshot_default_viewport_height',
|
|
287
|
+
960,
|
|
288
|
+
'env',
|
|
289
|
+
description="The default height for screenshots")
|
|
290
|
+
app.add_config_value(
|
|
291
|
+
'screenshot_default_browser',
|
|
292
|
+
'chromium',
|
|
293
|
+
'env',
|
|
294
|
+
description="The default browser for screenshots")
|
|
295
|
+
app.add_config_value(
|
|
296
|
+
'screenshot_default_full_page',
|
|
297
|
+
False,
|
|
298
|
+
'env',
|
|
299
|
+
description="Whether to take full page screenshots")
|
|
300
|
+
app.add_config_value(
|
|
301
|
+
'screenshot_default_color_scheme',
|
|
302
|
+
'null',
|
|
303
|
+
'env',
|
|
304
|
+
description="The default color scheme for screenshots")
|
|
305
|
+
app.add_config_value(
|
|
306
|
+
'screenshot_contexts', {},
|
|
307
|
+
'env',
|
|
308
|
+
types=[dict[str, str]],
|
|
309
|
+
description="A dict of paths to Playwright context build methods")
|
|
310
|
+
app.add_config_value(
|
|
311
|
+
'screenshot_default_headers', {},
|
|
312
|
+
'env',
|
|
313
|
+
description="The default headers to pass in requests")
|
|
314
|
+
app.add_config_value(
|
|
315
|
+
'screenshot_apps', {},
|
|
316
|
+
'env',
|
|
317
|
+
types=[dict[str, str]],
|
|
318
|
+
description="A dict of WSGI apps")
|
|
319
|
+
app.connect('config-inited', setup_apps)
|
|
320
|
+
app.connect('build-finished', teardown_apps)
|
|
321
|
+
return {
|
|
322
|
+
'version': importlib.metadata.version('sphinxcontrib-screenshot'),
|
|
323
|
+
'parallel_read_safe': True,
|
|
324
|
+
'parallel_write_safe': True,
|
|
325
|
+
}
|