Skip to content

Commit 089a8e7

Browse files
authored
Release v1.1.0 (#39)
- Not necessarily require to pass addon names to install them. - Fix bug propagating command line arguments to `pytest`, `blender` and `python` binaries. - Add `--blender-template` argument to pass a custom startup file. - Raise `ValueError` if no addons passed to install to `install_addons_from_dir` fixture.
1 parent f5caea6 commit 089a8e7

File tree

12 files changed

+121
-49
lines changed

12 files changed

+121
-49
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.0.2
2+
current_version = 1.1.0
33

44
[bumpversion:file:pytest_blender/__init__.py]
55

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ jobs:
5959
BLENDER_EXECUTABLE_PATH="$(cat _blender-executable-path.txt | tr -d '\n')"
6060
PYTHON_BLENDER_EXECUTABLE="$(pytest-blender --blender-executable $BLENDER_EXECUTABLE_PATH)"
6161
$PYTHON_BLENDER_EXECUTABLE -m ensurepip
62-
$PYTHON_BLENDER_EXECUTABLE -m pip install pytest
62+
pytest_version="$(< setup.cfg grep pytest== | head -n 1 | cut -d'=' -f3)"
63+
$PYTHON_BLENDER_EXECUTABLE -m pip install pytest==$pytest_version
6364
echo "::set-output name=blender-executable::$BLENDER_EXECUTABLE_PATH"
6465
- name: Test with Blender Python executable (no cache)
6566
run: pytest -svv -p no:cacheprovider --blender-executable "${{ steps.install-dependencies.outputs.blender-executable }}" tests

README.md

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,35 @@ tests/test_bpy_import.py . [100%]
8181
============================== 1 passed in 0.00s ===============================
8282
```
8383

84+
#### Arguments propagation
85+
86+
When you call `pytest`, all options like `--blender-executable` are passed
87+
to the `pytest` suite running `pytest-blender`. If you want to pass arguments
88+
to `blender` in its headless execution, add a `--` between `pytest` and
89+
`blender` arguments. If you want to pass arguments to the `python` Blender's
90+
interpreter, you need to add another `--` between arguments in a third group.
91+
92+
For example:
93+
94+
```sh
95+
pytest -svv --blender-executable ~/blender -- --enable-event-simulate -- -b
96+
```
97+
98+
In case that you don't want to pass arguments to `blender` but yes to `python`,
99+
use double arguments group separation (`-- --`):
100+
101+
```sh
102+
pytest -svv -- -- -b
103+
```
104+
105+
#### Load startup template
106+
107+
You can use the `--blender-template` argument to pass a custom startup file:
108+
109+
```sh
110+
pytest -svv --blender-template ~/.config/blender/2.93/config/startup.blend
111+
```
112+
84113
### Reference
85114

86115
#### Fixtures
@@ -109,9 +138,9 @@ Returns the version of the Python executable builtin in the Blender release of
109138
the currently running session.
110139

111140
<a name="install_addons_from_dir" href="#install_addons_from_dir">#</a>
112-
<b>install_addons_from_dir</b>(<i>addons_dir</i>, <i>addon_module_names</i>,
113-
<i>save_userpref=True</i>, <i>default_set=True</i>, <i>persistent=True</i>,
114-
<i>\*\*kwargs</i>)
141+
<b>install_addons_from_dir</b>(<i>addons_dir</i>,
142+
<i>addon_module_names=None</i>, <i>save_userpref=True</i>,
143+
<i>default_set=True</i>, <i>persistent=True</i>, <i>\*\*kwargs</i>)`list`
115144

116145
Function that installs and enables a set of addons whose modules are located in
117146
a directory. This function is designed to be executed before the pytest session
@@ -122,25 +151,30 @@ to disable them after the execution of the test suite:
122151
```python
123152
import os
124153

125-
ADDON_MODULE_NAMES = ["my_awesome_addon_module_name"]
154+
import pytest
126155

127156
@pytest.fixture(scope="session", autouse=True)
128157
def _register_addons(request, install_addons_from_dir, disable_addons):
129-
install_addons_from_dir(os.path.abspath("src"), ADDON_MODULE_NAMES)
158+
addon_module_names = install_addons_from_dir(os.path.abspath("src"))
130159
yield
131-
disable_addons(ADDON_MODULE_NAMES)
160+
disable_addons(addon_module_names)
132161
```
133162
- **addons_dir** (str) Directory in which are located the modules of the
134163
addons.
135-
- **addon_module_names** (list) Name of the addons modules (without the
136-
`.py` extension).
164+
- **addon_module_names** (list) Name of the addons modules. If not defined
165+
(default) all the python modules located in `addons_dir` directory whose names
166+
do not start with `__` will be considered addons.
137167
- **save_userpref** (bool) Save user preferences after installation.
138168
- **default_set** (bool) Set the user-preference calling `addon_utils.enable`.
139169
- **persistent** (bool) Ensure the addon is enabled for the entire session
140170
(after loading new files).
141171
- **\*\*kwargs** (dict) Subsecuent keyword arguments are passed to
142172
[`bpy.ops.preferences.addon_install`](https://docs.blender.org/api/current/bpy.ops.preferences.html#bpy.ops.preferences.addon_install).
143173

174+
Returns the addon module names as a list, ready to be passed to
175+
[`disable_addons`](https://github.com/mondeja/pytest-blender#disable_addons)
176+
function.
177+
144178
<a name="disable_addons" href="#disable_addons">#</a>
145179
<b>disable_addons</b>(<i>addon_module_names</i>, <i>save_userpref=True</i>,
146180
<i>default_set=True</i>, <i>\*\*kwargs</i>)

pytest_blender/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.0.2"
1+
__version__ = "1.1.0"

pytest_blender/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def build_parser():
2323
dest="blender_executable",
2424
nargs=1,
2525
default=which_blender_by_os,
26-
help="Blender executable location.",
26+
help="Custom Blender executable location for 'pytest-blender' plugin.",
2727
)
2828
return parser
2929

pytest_blender/plugin.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ def pytest_addoption(parser):
1717
default=which_blender_by_os,
1818
help="Blender executable location.",
1919
)
20+
parser.addoption(
21+
"--blender-template",
22+
nargs=1,
23+
default=None,
24+
help="Open Blender using an empty layout as start template.",
25+
)
2026

2127

2228
def _get_blender_executable(config):
@@ -26,24 +32,34 @@ def _get_blender_executable(config):
2632
return blender_executable[0]
2733

2834

35+
def _add_template_arg(config, args):
36+
template = config.getoption("--blender-template")
37+
if template:
38+
args.append(template[0])
39+
40+
2941
@pytest.hookimpl(tryfirst=True)
3042
def pytest_configure(config):
3143
pytest_help_opt = False
3244

3345
# build propagated CLI args
34-
propagated_cli_args = []
35-
_inside_root_invocation_arg = False
36-
for arg in sys.argv[1:]:
37-
if _inside_root_invocation_arg:
38-
_inside_root_invocation_arg = False
39-
continue
40-
elif arg == "--blender-executable":
41-
_inside_root_invocation_arg = True
46+
args_groups, args_group_index = ([], [], []), 0
47+
argv = sys.argv[1:]
48+
i = 0
49+
while i < len(argv):
50+
arg = argv[i]
51+
if arg in ["--blender-executable", "--blender-template"]:
52+
i += 2
4253
continue
4354
elif arg in ["-h", "--help"]:
4455
pytest_help_opt = True
4556
break
46-
propagated_cli_args.append(arg)
57+
elif arg == "--":
58+
args_group_index += 1
59+
i += 1
60+
continue
61+
args_groups[args_group_index].append(arg)
62+
i += 1
4763

4864
if pytest_help_opt:
4965
return
@@ -53,24 +69,33 @@ def pytest_configure(config):
5369
if not blender_executable:
5470
pytest.exit("'blender' executable not found.", returncode=1)
5571

72+
pytest_opts, blender_opts, python_opts = args_groups
73+
5674
# run pytest using blender
57-
proc = subprocess.Popen(
75+
args = [
76+
blender_executable,
77+
"-b",
78+
]
79+
80+
# template to open
81+
_add_template_arg(config, args)
82+
83+
args.extend(
5884
[
59-
blender_executable,
60-
"-b",
85+
*blender_opts, # propagate Blender command line arguments
6186
"--python",
6287
os.path.join(
6388
os.path.abspath(os.path.dirname(__file__)),
6489
"run_pytest.py",
6590
),
91+
*python_opts, # propagate Python command line arguments
6692
"--",
6793
"--pytest-blender-executable",
6894
blender_executable,
69-
*propagated_cli_args, # propagate command line arguments
70-
],
71-
stdout=sys.stdout,
72-
stderr=sys.stderr,
95+
*pytest_opts, # propagate Pytest command line arguments
96+
]
7397
)
98+
proc = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr)
7499
proc.communicate()
75100

76101
# hide "Exit:" message shown by pytest on exit

pytest_blender/run_pytest.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def install_addons_from_dir(self, request):
207207

208208
def _install_addons_from_dir(
209209
addons_dir,
210-
addon_module_names,
210+
addon_module_names=None,
211211
save_userpref=True,
212212
default_set=True,
213213
persistent=True,
@@ -216,8 +216,23 @@ def _install_addons_from_dir(
216216
import addon_utils # noqa F401
217217
import bpy # noqa F401
218218

219+
if addon_module_names is None:
220+
addon_module_names = [
221+
fname.rstrip(".py")
222+
for fname in os.listdir(addons_dir)
223+
if not fname.startswith("__")
224+
]
225+
if not addon_module_names:
226+
raise ValueError(
227+
f"Any addons found in '{addons_dir}' directory."
228+
)
229+
elif not addon_module_names:
230+
raise ValueError("You need to pass at least one addon to install.")
231+
219232
for addon_module_name in addon_module_names:
220-
addon_filepath = os.path.join(addons_dir, f"{addon_module_name}.py")
233+
addon_filepath = os.path.join(
234+
addons_dir, f"{addon_module_name.rstrip('.py')}.py"
235+
)
221236
bpy.ops.preferences.addon_install(filepath=addon_filepath, **kwargs)
222237
addon_utils.enable(
223238
addon_module_name,
@@ -227,6 +242,8 @@ def _install_addons_from_dir(
227242
if save_userpref:
228243
bpy.ops.wm.save_userpref()
229244

245+
return addon_module_names
246+
230247
return _install_addons_from_dir
231248

232249
@pytest.fixture(scope="session")

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pytest_blender
3-
version = 1.0.2
3+
version = 1.1.0
44
description = Blender Pytest plugin.
55
long_description = file: README.md
66
long_description_content_type = text/markdown

tests/addons/__init__.py

Whitespace-only changes.

tests/addons/pytest_blender_basic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
}
88

99

10-
class ObjectMoveX(bpy.types.Operator):
10+
class PytestBlenderObjectMoveX(bpy.types.Operator):
1111
"""My Object Moving Script"""
1212

1313
bl_idname = "object.move_x"
@@ -23,11 +23,11 @@ def execute(self, context):
2323

2424

2525
def register():
26-
bpy.utils.register_class(ObjectMoveX)
26+
bpy.utils.register_class(PytestBlenderObjectMoveX)
2727

2828

2929
def unregister():
30-
bpy.utils.unregister_class(ObjectMoveX)
30+
bpy.utils.unregister_class(PytestBlenderObjectMoveX)
3131

3232

3333
if __name__ == "__main__":

tests/conftest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io
44
import os
5+
import sys
56
from contextlib import redirect_stdout
67

78
import pytest
@@ -21,12 +22,11 @@ def _register_addons(request, install_addons_from_dir, disable_addons):
2122
os.path.abspath(os.path.dirname(__file__)),
2223
"addons",
2324
)
24-
addon_module_names = ["pytest_blender_basic"]
2525

2626
f = io.StringIO()
2727
with redirect_stdout(f):
28-
install_addons_from_dir(addons_dir, addon_module_names)
29-
30-
yield
31-
28+
addon_module_names = install_addons_from_dir(addons_dir)
29+
yield
30+
sys.stdout.write("\n")
31+
with redirect_stdout(f):
3232
disable_addons(addon_module_names)

tests/test_addons.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@ def test_basic_addon():
1717
import _bpy
1818
import addon_utils
1919

20-
_module_loaded = False
21-
for addon_module in addon_utils.modules():
22-
if addon_module.__name__ == "pytest_blender_basic":
23-
_module_loaded = True
24-
assert _module_loaded
20+
installed_addons = [addon.__name__ for addon in addon_utils.modules()]
21+
assert "pytest_blender_basic" in installed_addons
22+
assert "__init__" not in installed_addons
2523

26-
_operator_class_loaded = False
27-
for operator_cls in _bpy.types.Operator.__subclasses__():
28-
if operator_cls.__name__ == "ObjectMoveX":
29-
_operator_class_loaded = True
30-
assert _operator_class_loaded
24+
operator_classes = [cls.__name__ for cls in _bpy.types.Operator.__subclasses__()]
25+
assert "PytestBlenderObjectMoveX" in operator_classes

0 commit comments

Comments
 (0)