sphinxcontrib-screenshot 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl

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.

@@ -13,17 +13,25 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import hashlib
16
+ import importlib
17
+ import importlib.metadata
16
18
  import os
19
+ import threading
17
20
  import typing
21
+ import wsgiref.simple_server
18
22
  from concurrent.futures import ThreadPoolExecutor
19
23
  from urllib.parse import urlparse
20
24
 
21
25
  from docutils import nodes
22
26
  from docutils.parsers.rst import directives
23
27
  from docutils.statemachine import ViewList
28
+ from playwright._impl._helper import ColorScheme
29
+ from playwright.sync_api import Browser, BrowserContext
24
30
  from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
25
31
  from playwright.sync_api import sync_playwright
32
+ from portpicker import pick_unused_port
26
33
  from sphinx.application import Sphinx
34
+ from sphinx.config import Config
27
35
  from sphinx.util.docutils import SphinxDirective
28
36
 
29
37
  Meta = typing.TypedDict('Meta', {
@@ -32,8 +40,6 @@ Meta = typing.TypedDict('Meta', {
32
40
  'parallel_write_safe': bool
33
41
  })
34
42
 
35
- __version__ = '0.1.3'
36
-
37
43
 
38
44
  class ScreenshotDirective(SphinxDirective):
39
45
  """Sphinx Screenshot Dirctive.
@@ -92,17 +98,27 @@ class ScreenshotDirective(SphinxDirective):
92
98
  required_arguments = 1 # URL
93
99
  has_content = True
94
100
  option_spec = {
101
+ 'browser': str,
95
102
  'height': directives.positive_int,
96
103
  'width': directives.positive_int,
97
104
  'caption': directives.unchanged,
98
105
  'figclass': directives.unchanged,
99
106
  'pdf': directives.flag,
107
+ 'color-scheme': str,
108
+ 'full-page': directives.flag,
109
+ 'context': str,
110
+ 'headers': directives.unchanged,
100
111
  }
101
112
  pool = ThreadPoolExecutor()
102
113
 
103
114
  @staticmethod
104
- def take_screenshot(url: str, width: int, height: int, filepath: str,
105
- init_script: str, interactions: str, generate_pdf: bool):
115
+ def take_screenshot(
116
+ url: str, browser_name: str, width: int, height: int, filepath: str,
117
+ init_script: str, interactions: str, generate_pdf: bool,
118
+ color_scheme: ColorScheme, full_page: bool,
119
+ context_builder: typing.Optional[typing.Callable[[Browser, str, str],
120
+ BrowserContext]],
121
+ headers: dict):
106
122
  """Takes a screenshot with Playwright's Chromium browser.
107
123
 
108
124
  Args:
@@ -116,15 +132,30 @@ class ScreenshotDirective(SphinxDirective):
116
132
  interactions (str): JavaScript code to run before taking the screenshot
117
133
  after the page was loaded.
118
134
  generate_pdf (bool): Generate a PDF file along with the screenshot.
135
+ color_scheme (str): The preferred color scheme. Can be 'light' or 'dark'.
136
+ context: A method to build the Playwright context.
119
137
  """
120
138
  with sync_playwright() as playwright:
121
- browser = playwright.chromium.launch()
122
- page = browser.new_page()
139
+ browser = getattr(playwright, browser_name).launch()
140
+
141
+ if context_builder:
142
+ try:
143
+ context = context_builder(browser, url, color_scheme)
144
+ except PlaywrightTimeoutError:
145
+ raise RuntimeError(
146
+ 'Timeout error occured at %s in executing py init script %s' %
147
+ (url, context_builder.__name__))
148
+ else:
149
+ context = browser.new_context(color_scheme=color_scheme)
150
+
151
+ page = context.new_page()
123
152
  page.set_default_timeout(10000)
124
153
  page.set_viewport_size({'width': width, 'height': height})
154
+
125
155
  try:
126
156
  if init_script:
127
157
  page.add_init_script(init_script)
158
+ page.set_extra_http_headers(headers)
128
159
  page.goto(url)
129
160
  page.wait_for_load_state('networkidle')
130
161
 
@@ -135,7 +166,7 @@ class ScreenshotDirective(SphinxDirective):
135
166
  except PlaywrightTimeoutError:
136
167
  raise RuntimeError('Timeout error occured at %s in executing\n%s' %
137
168
  (url, interactions))
138
- page.screenshot(path=filepath)
169
+ page.screenshot(path=filepath, full_page=full_page)
139
170
  if generate_pdf:
140
171
  page.emulate_media(media='screen')
141
172
  root, ext = os.path.splitext(filepath)
@@ -143,6 +174,12 @@ class ScreenshotDirective(SphinxDirective):
143
174
  page.close()
144
175
  browser.close()
145
176
 
177
+ def evaluate_substitutions(self, text: str) -> str:
178
+ substitutions = self.state.document.substitution_defs
179
+ for key, value in substitutions.items():
180
+ text = text.replace(f"|{key}|", value.astext())
181
+ return text
182
+
146
183
  def run(self) -> typing.List[nodes.Node]:
147
184
  screenshot_init_script: str = self.env.config.screenshot_init_script or ''
148
185
 
@@ -151,28 +188,56 @@ class ScreenshotDirective(SphinxDirective):
151
188
  os.makedirs(ss_dirpath, exist_ok=True)
152
189
 
153
190
  # Parse parameters
154
- url = self.arguments[0]
155
- height = self.options.get('height', 960)
156
- width = self.options.get('width', 1280)
191
+ raw_url = self.arguments[0]
192
+ url = self.evaluate_substitutions(raw_url)
193
+ browser = self.options.get('browser',
194
+ self.env.config.screenshot_default_browser)
195
+ height = self.options.get('height',
196
+ self.env.config.screenshot_default_height)
197
+ width = self.options.get('width', self.env.config.screenshot_default_width)
198
+ color_scheme = self.options.get(
199
+ 'color-scheme', self.env.config.screenshot_default_color_scheme)
157
200
  caption_text = self.options.get('caption', '')
158
201
  figclass = self.options.get('figclass', '')
159
202
  pdf = 'pdf' in self.options
203
+ full_page = ('full-page' in self.options or
204
+ self.env.config.screenshot_default_full_page)
205
+ context = self.options.get('context', '')
160
206
  interactions = '\n'.join(self.content)
207
+ headers = self.options.get('headers', '')
208
+
209
+ request_headers = {**self.env.config.screenshot_default_headers}
210
+ if headers:
211
+ for header in headers.strip().split("\n"):
212
+ name, value = header.split(" ", 1)
213
+ request_headers[name] = value
161
214
 
162
215
  if urlparse(url).scheme not in {'http', 'https'}:
163
216
  raise RuntimeError(
164
217
  f'Invalid URL: {url}. Only HTTP/HTTPS URLs are supported.')
165
218
 
166
219
  # Generate filename based on hash of parameters
167
- hash_input = f'{url}_{height}_{width}_{interactions}'
220
+ hash_input = "_".join([
221
+ raw_url, browser,
222
+ str(height),
223
+ str(width), color_scheme, context, interactions,
224
+ str(full_page)
225
+ ])
168
226
  filename = hashlib.md5(hash_input.encode()).hexdigest() + '.png'
169
227
  filepath = os.path.join(ss_dirpath, filename)
170
228
 
229
+ if context:
230
+ context_builder_path = self.config.screenshot_contexts[context]
231
+ context_builder = resolve_python_method(context_builder_path)
232
+ else:
233
+ context_builder = None
234
+
171
235
  # Check if the file already exists. If not, take a screenshot
172
236
  if not os.path.exists(filepath):
173
- fut = self.pool.submit(ScreenshotDirective.take_screenshot, url, width,
174
- height, filepath, screenshot_init_script,
175
- interactions, pdf)
237
+ fut = self.pool.submit(ScreenshotDirective.take_screenshot, url, browser,
238
+ width, height, filepath, screenshot_init_script,
239
+ interactions, pdf, color_scheme, full_page,
240
+ context_builder, request_headers)
176
241
  fut.result()
177
242
 
178
243
  # Create image and figure nodes
@@ -195,11 +260,86 @@ class ScreenshotDirective(SphinxDirective):
195
260
  return [figure_node]
196
261
 
197
262
 
263
+ app_threads = {}
264
+
265
+
266
+ def resolve_python_method(import_path: str):
267
+ module_path, method_name = import_path.split(":")
268
+ module = importlib.import_module(module_path)
269
+ method = getattr(module, method_name)
270
+ return method
271
+
272
+
273
+ def setup_apps(app: Sphinx, config: Config):
274
+ """Start the WSGI application threads.
275
+
276
+ A new replacement is created for each WSGI app."""
277
+ for wsgi_app_name, wsgi_app_path in config.screenshot_apps.items():
278
+ port = pick_unused_port()
279
+ config.rst_prolog = (
280
+ config.rst_prolog or
281
+ "") + f"\n.. |{wsgi_app_name}| replace:: http://localhost:{port}\n"
282
+ app_builder = resolve_python_method(wsgi_app_path)
283
+ wsgi_app = app_builder(app)
284
+ httpd = wsgiref.simple_server.make_server("localhost", port, wsgi_app)
285
+ thread = threading.Thread(target=httpd.serve_forever)
286
+ thread.start()
287
+ app_threads[wsgi_app_name] = (httpd, thread)
288
+
289
+
290
+ def teardown_apps(app: Sphinx, exception: typing.Optional[Exception]):
291
+ """Shut down the WSGI application threads."""
292
+ for httpd, thread in app_threads.values():
293
+ httpd.shutdown()
294
+ thread.join()
295
+
296
+
198
297
  def setup(app: Sphinx) -> Meta:
199
298
  app.add_directive('screenshot', ScreenshotDirective)
200
299
  app.add_config_value('screenshot_init_script', '', 'env')
300
+ app.add_config_value(
301
+ 'screenshot_default_width',
302
+ 1280,
303
+ 'env',
304
+ description="The default width for screenshots")
305
+ app.add_config_value(
306
+ 'screenshot_default_height',
307
+ 960,
308
+ 'env',
309
+ description="The default height for screenshots")
310
+ app.add_config_value(
311
+ 'screenshot_default_browser',
312
+ 'chromium',
313
+ 'env',
314
+ description="The default browser for screenshots")
315
+ app.add_config_value(
316
+ 'screenshot_default_full_page',
317
+ False,
318
+ 'env',
319
+ description="Whether to take full page screenshots")
320
+ app.add_config_value(
321
+ 'screenshot_default_color_scheme',
322
+ 'null',
323
+ 'env',
324
+ description="The default color scheme for screenshots")
325
+ app.add_config_value(
326
+ 'screenshot_contexts', {},
327
+ 'env',
328
+ types=[dict[str, str]],
329
+ description="A dict of paths to Playwright context build methods")
330
+ app.add_config_value(
331
+ 'screenshot_default_headers', {},
332
+ 'env',
333
+ description="The default headers to pass in requests")
334
+ app.add_config_value(
335
+ 'screenshot_apps', {},
336
+ 'env',
337
+ types=[dict[str, str]],
338
+ description="A dict of WSGI apps")
339
+ app.connect('config-inited', setup_apps)
340
+ app.connect('build-finished', teardown_apps)
201
341
  return {
202
- 'version': __version__,
342
+ 'version': importlib.metadata.version('sphinxcontrib-screenshot'),
203
343
  'parallel_read_safe': True,
204
344
  'parallel_write_safe': True,
205
345
  }
@@ -1,103 +1,64 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: sphinxcontrib-screenshot
3
- Version: 0.1.3
4
- Summary: A Shpinx extension to embed webpage screenshots.
5
- Home-page: https://github.com/tushuhei/sphinxcontrib-screenshot/
6
- Author: Shuhei Iitsuka
7
- Author-email: tushuhei@gmail.com
3
+ Version: 0.1.4
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
- ![Example screenshot](https://raw.githubusercontent.com/tushuhei/sphinxcontrib-screenshot/main/example.png)
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
53
  ```rst
67
54
  .. screenshot:: http://www.example.com
55
+ :browser: chromium
68
56
  :width: 1280
69
57
  :height: 960
58
+ :color-scheme: dark
70
59
  ```
71
60
 
72
- You can include a caption for the screenshot's `figure` directive.
73
-
74
- ```rst
75
- .. screenshot:: http://www.example.com
76
- :caption: This is a screenshot for www.example.com
77
- ```
78
-
79
- You can describe the interaction that you want to have with the webpage before taking a screenshot in JavaScript.
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"
99
- ```
100
-
61
+ Read more in the [documentation](https://sphinxcontrib-screenshot.readthedocs.io).
101
62
 
102
63
  ## Notes
103
64
 
@@ -0,0 +1,6 @@
1
+ sphinxcontrib/screenshot.py,sha256=eJEF1qfGGo1uwyTovYHXx4Ewz2fXmrxDSsQSdLJ0O-0,11863
2
+ sphinxcontrib_screenshot-0.1.4.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
3
+ sphinxcontrib_screenshot-0.1.4.dist-info/METADATA,sha256=XpzmVDGTDfL-AxKUv7jIV3wQ_phEGc-OLxWU-wG7-o0,2850
4
+ sphinxcontrib_screenshot-0.1.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
5
+ sphinxcontrib_screenshot-0.1.4.dist-info/top_level.txt,sha256=VJrV3_vaiKQVgVpR0I1iecxoO0drzGu-M0j40PVP2QQ,14
6
+ sphinxcontrib_screenshot-0.1.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- sphinxcontrib/screenshot.py,sha256=rTcbf2bx5DiRIGKcA8dL7jbi-GwfrW7-aXU83M5vAlY,6829
2
- sphinxcontrib_screenshot-0.1.3.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
3
- sphinxcontrib_screenshot-0.1.3.dist-info/METADATA,sha256=T6kkviJZwZe0ZtYllM4YwhlI9qiKtoTbnI6OAcxlvII,3652
4
- sphinxcontrib_screenshot-0.1.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
5
- sphinxcontrib_screenshot-0.1.3.dist-info/top_level.txt,sha256=VJrV3_vaiKQVgVpR0I1iecxoO0drzGu-M0j40PVP2QQ,14
6
- sphinxcontrib_screenshot-0.1.3.dist-info/RECORD,,