-
Notifications
You must be signed in to change notification settings - Fork 1.5k
DOC: Execute docs examples in CI #3507
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
base: main
Are you sure you want to change the base?
Changes from 57 commits
40c08bd
0a8e021
6d16058
063950f
a8a2a18
4b42e33
5d46e69
497c6e2
50aa2ab
a2d191b
485d6d3
5885cbd
8dbfc4a
36c97d0
5c1ba4d
748b2cd
24409ba
4799520
bee4ca7
2f67992
01ff206
c816752
53975f1
f4f680d
bd09de3
00a7f11
f2884d8
b81f29d
a94a2b2
996366c
e8afc04
cb7f018
f7a4337
eea1223
e4afcc3
65ce130
1ec3d7f
75ca54f
1546a14
a530f11
9e7083e
62792f2
2c58519
cd9a5eb
5d11a51
6bac8c3
2035c50
691ba58
7d5d9e5
79185c4
48f9833
e586c79
7ebf235
2a69f06
97bd980
540c13b
4bd5a14
a41917e
9cefe93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |||||
| import os | ||||||
| import shutil | ||||||
| import sys | ||||||
| from pathlib import Path | ||||||
|
|
||||||
| sys.path.insert(0, os.path.abspath(".")) | ||||||
| sys.path.insert(0, os.path.abspath("../")) | ||||||
|
|
@@ -53,6 +54,7 @@ | |||||
| "sphinx.ext.mathjax", | ||||||
| "sphinx.ext.viewcode", | ||||||
| "sphinx.ext.napoleon", | ||||||
| "sphinx.ext.doctest", | ||||||
| # External | ||||||
| "myst_parser", | ||||||
| ] | ||||||
|
|
@@ -132,3 +134,93 @@ | |||||
| napoleon_numpy_docstring = False # Explicitly prefer Google style docstring | ||||||
| napoleon_use_param = True # for type hint support | ||||||
| napoleon_use_rtype = False # False, so the return type is inline with the description. | ||||||
|
|
||||||
| # -- Options for Doctest ------------------------------------------------------ | ||||||
|
|
||||||
| # Most of doc examples use hardcoded input and output file names. | ||||||
| # To execute these examples real files need to be read and written. | ||||||
| # | ||||||
| # By default, documentation examples run with the working directory set to where | ||||||
| # "sphinx-build" command was invoked. To avoid relative paths in docs and to | ||||||
| # allow to run "sphinx-build" command from any directory, we modify the current | ||||||
| # working directory in each tested file. Tests are executed against our | ||||||
| # temporary directory where we have copied all nessesary resources. | ||||||
| # | ||||||
| # Each doc page that requires file operations must use "testsetup" directive | ||||||
| # to call "pypdf_test_setup" function to prepare the test environment for that | ||||||
| # page. | ||||||
| # | ||||||
| # def pypdf_test_setup(group: str, resources: dict[str, str] = {}) -> None | ||||||
| # | ||||||
| # Args: | ||||||
| # group: A unique name for group of tests. Typically we group tests by doc page. | ||||||
| # For each doc page we create a test folder under | ||||||
| # "_build/doctest/pypdf_test/<group>". This allows to avoid file name conflicts | ||||||
| # between different doc pages. | ||||||
| # resources: A dictionary of source files to copy into the test folder. | ||||||
| # Key is the destination file name (relative to the test folder). | ||||||
| # Value is the source file path (relative to the root folder). | ||||||
| # | ||||||
| # Examples: | ||||||
| # ```{testsetup} | ||||||
| # pypdf_test_setup("user/add-javascript", { | ||||||
| # "example.pdf": "../resources/example.pdf", | ||||||
| # }) | ||||||
| # ``` | ||||||
|
|
||||||
| pypdf_test_src_root_dir = os.path.abspath(".") | ||||||
| pypdf_test_dst_root_dir = os.path.abspath("_build/doctest/pypdf_test") | ||||||
| if Path(pypdf_test_dst_root_dir).exists(): | ||||||
| shutil.rmtree(pypdf_test_dst_root_dir) | ||||||
| Path(pypdf_test_dst_root_dir).mkdir(parents=True) | ||||||
|
|
||||||
| doctest_global_setup = f""" | ||||||
| def pypdf_test_global_setup(): | ||||||
| import os | ||||||
| import shutil | ||||||
| from pathlib import Path | ||||||
| src_root_dir = {pypdf_test_src_root_dir.__repr__()} | ||||||
| dst_root_dir = {pypdf_test_dst_root_dir.__repr__()} | ||||||
| global pypdf_test_orig_dir | ||||||
| pypdf_test_orig_dir = os.getcwd() | ||||||
| os.chdir(dst_root_dir) | ||||||
| global pypdf_test_setup | ||||||
| def pypdf_test_setup(group: str, resources: dict[str, str] = {{}}) -> None: | ||||||
| dst_dir = os.path.join(dst_root_dir, group) | ||||||
| Path(dst_dir).mkdir(parents=True) | ||||||
| os.chdir(dst_dir) | ||||||
| for (dst_path, src_path) in resources.items(): | ||||||
| src = os.path.normpath(os.path.join(src_root_dir, src_path)) | ||||||
| dst = os.path.join(dst_dir, dst_path) | ||||||
| shutil.copyfile(src, dst) | ||||||
| pypdf_test_global_setup() | ||||||
| """ | ||||||
|
|
||||||
| doctest_global_cleanup = f""" | ||||||
| def pypdf_test_global_cleanup(): | ||||||
| import os | ||||||
| dst_root_dir = {pypdf_test_dst_root_dir.__repr__()} | ||||||
| os.chdir(pypdf_test_orig_dir) | ||||||
| has_files = False | ||||||
| for file_name in os.listdir(dst_root_dir): | ||||||
| file = os.path.join(dst_root_dir, file_name) | ||||||
| if os.path.isfile(file): | ||||||
| if not has_files: | ||||||
| print("Docs page was not configured propery for running code examples") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess these should print to stderr instead of stdout? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case of stderr |
||||||
| print("Please use 'pypdf_test_setup' function in 'testsetup' directive") | ||||||
| print("Deleting unexpected file(s) in " + dst_root_dir) | ||||||
| has_files = True | ||||||
| print(f"- {{file_name}}") | ||||||
| os.remove(file) # We should not affect other tests | ||||||
|
||||||
| os.remove(file) # We should not affect other tests | |
| os.remove(file) # Avoid side effects on other tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,20 +10,28 @@ The process of stamping and watermarking is the same, you just need to set `over | |
|
|
||
| You can use {func}`~pypdf._page.PageObject.merge_page` if you don't need to transform the stamp: | ||
|
|
||
| ```python | ||
| ```{testsetup} | ||
| pypdf_test_setup("user/add-watermark", { | ||
| "crazyones.pdf": "../resources/crazyones.pdf", | ||
| "nup-source.png": "../docs/user/nup-source.png", | ||
| "jpeg.pdf": "../resources/jpeg.pdf", | ||
| }) | ||
| ``` | ||
|
|
||
| ```{testcode} | ||
| from pypdf import PdfReader, PdfWriter | ||
|
|
||
| stamp = PdfReader("bg.pdf").pages[0] | ||
| writer = PdfWriter(clone_from="source.pdf") | ||
| stamp = PdfReader("jpeg.pdf").pages[0] | ||
| writer = PdfWriter(clone_from="crazyones.pdf") | ||
| for page in writer.pages: | ||
| page.merge_page(stamp, over=False) # here set to False for watermarking | ||
|
|
||
| writer.write("out.pdf") | ||
| writer.write("out-underlay.pdf") | ||
|
||
| ``` | ||
|
|
||
| Otherwise use {func}`~pypdf._page.PageObject.merge_transformed_page` with {class}`~pypdf.Transformation` if you need to translate, rotate, scale, etc. the stamp before merging it to the content page. | ||
|
|
||
| ```python | ||
| ```{testcode} | ||
| from pathlib import Path | ||
| from typing import List, Union | ||
|
|
||
|
|
@@ -52,7 +60,7 @@ def stamp( | |
| writer.write(pdf_result) | ||
|
|
||
|
|
||
| stamp("example.pdf", "stamp.pdf", "out.pdf") | ||
| stamp("crazyones.pdf", "jpeg.pdf", "out-scale.pdf") | ||
| ``` | ||
|
|
||
| If you are experiencing wrongly rotated watermarks/stamps, try to use | ||
|
|
@@ -73,7 +81,7 @@ However, you can easily convert an image to PDF image using | |
| [Pillow](https://pypi.org/project/Pillow/). | ||
|
|
||
|
|
||
| ```python | ||
| ```{testcode} | ||
| from io import BytesIO | ||
| from pathlib import Path | ||
| from typing import List, Union | ||
|
|
@@ -111,9 +119,8 @@ def stamp_img( | |
| Transformation(), | ||
| ) | ||
|
|
||
| with open(pdf_result, "wb") as fp: | ||
| writer.write(fp) | ||
| writer.write(pdf_result) | ||
|
|
||
|
|
||
| stamp_img("example.pdf", "example.png", "out.pdf") | ||
| stamp_img("crazyones.pdf", "nup-source.png", "out-image.pdf") | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid using the (previously) builtin name
fileto avoid confusion.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done... thanks. I've learned something new 👍