Simplify tests by replacing output capture with asserts where possible

The C++ part of the test code is modified to achieve this. As a result,
this kind of test:

```python
with capture:
    kw_func1(5, y=10)
assert capture == "kw_func(x=5, y=10)"
```

can be replaced with a simple:

`assert kw_func1(5, y=10) == "x=5, y=10"`
This commit is contained in:
Dean Moldovan 2016-08-12 22:28:31 +02:00
parent a0c1ccf0a9
commit 665e8804f3
18 changed files with 258 additions and 405 deletions

View File

@ -12,18 +12,16 @@
#include <pybind11/functional.h> #include <pybind11/functional.h>
bool test_callback1(py::object func) { py::object test_callback1(py::object func) {
func(); return func();
return false;
} }
int test_callback2(py::object func) { py::tuple test_callback2(py::object func) {
py::object result = func("Hello", 'x', true, 5); return func("Hello", 'x', true, 5);
return result.cast<int>();
} }
void test_callback3(const std::function<int(int)> &func) { std::string test_callback3(const std::function<int(int)> &func) {
cout << "func(43) = " << func(43)<< std::endl; return "func(43) = " + std::to_string(func(43));
} }
std::function<int(int)> test_callback4() { std::function<int(int)> test_callback4() {
@ -37,27 +35,24 @@ py::cpp_function test_callback5() {
int dummy_function(int i) { return i + 1; } int dummy_function(int i) { return i + 1; }
int dummy_function2(int i, int j) { return i + j; } int dummy_function2(int i, int j) { return i + j; }
std::function<int(int)> roundtrip(std::function<int(int)> f) { std::function<int(int)> roundtrip(std::function<int(int)> f, bool expect_none = false) {
if (!f) if (expect_none && f) {
std::cout << "roundtrip (got None).." << std::endl; throw std::runtime_error("Expected None to be converted to empty std::function");
else }
std::cout << "roundtrip.." << std::endl;
return f; return f;
} }
void test_dummy_function(const std::function<int(int)> &f) { std::string test_dummy_function(const std::function<int(int)> &f) {
using fn_type = int (*)(int); using fn_type = int (*)(int);
auto result = f.target<fn_type>(); auto result = f.target<fn_type>();
if (!result) { if (!result) {
std::cout << "could not convert to a function pointer." << std::endl;
auto r = f(1); auto r = f(1);
std::cout << "eval(1) = " << r << std::endl; return "can't convert to function pointer: eval(1) = " + std::to_string(r);
} else if (*result == dummy_function) { } else if (*result == dummy_function) {
std::cout << "argument matches dummy_function" << std::endl;
auto r = (*result)(1); auto r = (*result)(1);
std::cout << "eval(1) = " << r << std::endl; return "matches dummy_function: eval(1) = " + std::to_string(r);
} else { } else {
std::cout << "argument does NOT match dummy_function. This should never happen!" << std::endl; return "argument does NOT match dummy_function. This should never happen!";
} }
} }
@ -96,7 +91,7 @@ void init_ex_callbacks(py::module &m) {
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function); m.def("dummy_function", &dummy_function);
m.def("dummy_function2", &dummy_function2); m.def("dummy_function2", &dummy_function2);
m.def("roundtrip", &roundtrip); m.def("roundtrip", &roundtrip, py::arg("f"), py::arg("expect_none")=false);
m.def("test_dummy_function", &test_dummy_function); m.def("test_dummy_function", &test_dummy_function);
// Export the payload constructor statistics for testing purposes: // Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>); m.def("payload_cstats", &ConstructorStats::get<Payload>);

View File

@ -1,70 +1,51 @@
import pytest import pytest
def test_inheritance(capture, msg): def test_inheritance(msg):
from pybind11_tests import Pet, Dog, Rabbit, dog_bark, pet_print from pybind11_tests import Pet, Dog, Rabbit, dog_bark, pet_name_species
roger = Rabbit('Rabbit') roger = Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot" assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
with capture: assert pet_name_species(roger) == "Rabbit is a parrot"
pet_print(roger)
assert capture == "Rabbit is a parrot"
polly = Pet('Polly', 'parrot') polly = Pet('Polly', 'parrot')
assert polly.name() + " is a " + polly.species() == "Polly is a parrot" assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
with capture: assert pet_name_species(polly) == "Polly is a parrot"
pet_print(polly)
assert capture == "Polly is a parrot"
molly = Dog('Molly') molly = Dog('Molly')
assert molly.name() + " is a " + molly.species() == "Molly is a dog" assert molly.name() + " is a " + molly.species() == "Molly is a dog"
with capture: assert pet_name_species(molly) == "Molly is a dog"
pet_print(molly)
assert capture == "Molly is a dog"
with capture: assert dog_bark(molly) == "Woof!"
dog_bark(molly)
assert capture == "Woof!"
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
dog_bark(polly) dog_bark(polly)
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported: Incompatible function arguments. The following argument types are supported:
1. (arg0: m.Dog) -> None 1. (arg0: m.Dog) -> str
Invoked with: <m.Pet object at 0> Invoked with: <m.Pet object at 0>
""" """
def test_callbacks(capture): def test_callbacks():
from functools import partial from functools import partial
from pybind11_tests import (test_callback1, test_callback2, test_callback3, from pybind11_tests import (test_callback1, test_callback2, test_callback3,
test_callback4, test_callback5) test_callback4, test_callback5)
def func1(): def func1():
print('Callback function 1 called!') return "func1"
def func2(a, b, c, d): def func2(a, b, c, d):
print('Callback function 2 called : {}, {}, {}, {}'.format(a, b, c, d)) return "func2", a, b, c, d
return d
def func3(a): def func3(a):
print('Callback function 3 called : {}'.format(a)) return "func3({})".format(a)
with capture: assert test_callback1(func1) == "func1"
assert test_callback1(func1) is False assert test_callback2(func2) == ("func2", "Hello", "x", True, 5)
assert capture == "Callback function 1 called!" assert test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4)
with capture: assert test_callback1(partial(func3, "partial")) == "func3(partial)"
assert test_callback2(func2) == 5 assert test_callback3(lambda i: i + 1) == "func(43) = 44"
assert capture == "Callback function 2 called : Hello, x, True, 5"
with capture:
assert test_callback1(partial(func2, "Hello", "from", "partial", "object")) is False
assert capture == "Callback function 2 called : Hello, from, partial, object"
with capture:
assert test_callback1(partial(func3, "Partial object with one argument")) is False
assert capture == "Callback function 3 called : Partial object with one argument"
with capture:
test_callback3(lambda i: i + 1)
assert capture == "func(43) = 44"
f = test_callback4() f = test_callback4()
assert f(43) == 44 assert f(43) == 44
@ -82,49 +63,27 @@ def test_lambda_closure_cleanup():
assert cstats.move_constructions >= 1 assert cstats.move_constructions >= 1
def test_cpp_function_roundtrip(capture): def test_cpp_function_roundtrip():
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
from pybind11_tests import dummy_function, dummy_function2, test_dummy_function, roundtrip from pybind11_tests import dummy_function, dummy_function2, test_dummy_function, roundtrip
with capture: assert test_dummy_function(dummy_function) == "matches dummy_function: eval(1) = 2"
test_dummy_function(dummy_function) assert test_dummy_function(roundtrip(dummy_function)) == "matches dummy_function: eval(1) = 2"
assert capture == """ assert roundtrip(None, expect_none=True) is None
argument matches dummy_function assert test_dummy_function(lambda x: x + 2) == "can't convert to function pointer: eval(1) = 3"
eval(1) = 2
"""
with capture:
test_dummy_function(roundtrip(dummy_function))
assert capture == """
roundtrip..
argument matches dummy_function
eval(1) = 2
"""
with capture:
assert roundtrip(None) is None
assert capture == "roundtrip (got None).."
with capture:
test_dummy_function(lambda x: x + 2)
assert capture == """
could not convert to a function pointer.
eval(1) = 3
"""
with capture:
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
test_dummy_function(dummy_function2) test_dummy_function(dummy_function2)
assert "Incompatible function arguments" in str(excinfo.value) assert "Incompatible function arguments" in str(excinfo.value)
assert capture == "could not convert to a function pointer."
with capture:
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
test_dummy_function(lambda x, y: x + y) test_dummy_function(lambda x, y: x + y)
assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument", assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument",
"takes exactly 2 arguments")) "takes exactly 2 arguments"))
assert capture == "could not convert to a function pointer."
def test_function_signatures(doc): def test_function_signatures(doc):
from pybind11_tests import test_callback3, test_callback4 from pybind11_tests import test_callback3, test_callback4
assert doc(test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> None" assert doc(test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
assert doc(test_callback4) == "test_callback4() -> Callable[[int], int]" assert doc(test_callback4) == "test_callback4() -> Callable[[int], int]"

View File

@ -27,27 +27,24 @@ public:
}; };
static EMode test_function(EMode mode) { static EMode test_function(EMode mode) {
std::cout << "ExampleWithEnum::test_function(enum=" << mode << ")" << std::endl;
return mode; return mode;
} }
}; };
bool test_function1() { std::string test_function1() {
std::cout << "test_function()" << std::endl; return "test_function()";
return false;
} }
void test_function2(EMyEnumeration k) { std::string test_function2(EMyEnumeration k) {
std::cout << "test_function(enum=" << k << ")" << std::endl; return "test_function(enum=" + std::to_string(k) + ")";
} }
float test_function3(int i) { std::string test_function3(int i) {
std::cout << "test_function(" << i << ")" << std::endl; return "test_function(" + std::to_string(i) + ")";
return (float) i / 2.f;
} }
void test_ecenum(ECMyEnum z) { std::string test_ecenum(ECMyEnum z) {
std::cout << "test_ecenum(ECMyEnum::" << (z == ECMyEnum::Two ? "Two" : "Three") << ")" << std::endl; return "test_ecenum(ECMyEnum::" + std::string(z == ECMyEnum::Two ? "Two" : "Three") + ")";
} }
py::bytes return_bytes() { py::bytes return_bytes() {
@ -55,10 +52,14 @@ py::bytes return_bytes() {
return std::string(data, 4); return std::string(data, 4);
} }
void print_bytes(py::bytes bytes) { std::string print_bytes(py::bytes bytes) {
std::string value = (std::string) bytes; std::string ret = "bytes[";
for (size_t i = 0; i < value.length(); ++i) const auto value = static_cast<std::string>(bytes);
std::cout << "bytes[" << i << "]=" << (int) value[i] << std::endl; for (size_t i = 0; i < value.length(); ++i) {
ret += std::to_string(static_cast<int>(value[i])) + " ";
}
ret.back() = ']';
return ret;
} }
void init_ex_constants_and_functions(py::module &m) { void init_ex_constants_and_functions(py::module &m) {

View File

@ -7,20 +7,13 @@ def test_constants():
assert some_constant == 14 assert some_constant == 14
def test_function_overloading(capture): def test_function_overloading():
from pybind11_tests import EMyEnumeration, test_function from pybind11_tests import EMyEnumeration, test_function
with capture: assert test_function() == "test_function()"
assert test_function() is False assert test_function(7) == "test_function(7)"
assert test_function(7) == 3.5 assert test_function(EMyEnumeration.EFirstEntry) == "test_function(enum=1)"
assert test_function(EMyEnumeration.EFirstEntry) is None assert test_function(EMyEnumeration.ESecondEntry) == "test_function(enum=2)"
assert test_function(EMyEnumeration.ESecondEntry) is None
assert capture == """
test_function()
test_function(7)
test_function(enum=1)
test_function(enum=2)
"""
def test_unscoped_enum(): def test_unscoped_enum():
@ -39,16 +32,12 @@ def test_unscoped_enum():
assert str(EMyEnumeration(2)) == "EMyEnumeration.ESecondEntry" assert str(EMyEnumeration(2)) == "EMyEnumeration.ESecondEntry"
def test_scoped_enum(capture): def test_scoped_enum():
from pybind11_tests import ECMyEnum, test_ecenum from pybind11_tests import ECMyEnum, test_ecenum
with capture: assert test_ecenum(ECMyEnum.Three) == "test_ecenum(ECMyEnum::Three)"
test_ecenum(ECMyEnum.Three)
assert capture == "test_ecenum(ECMyEnum::Three)"
z = ECMyEnum.Two z = ECMyEnum.Two
with capture: assert test_ecenum(z) == "test_ecenum(ECMyEnum::Two)"
test_ecenum(z)
assert capture == "test_ecenum(ECMyEnum::Two)"
# expected TypeError exceptions for scoped enum ==/!= int comparisons # expected TypeError exceptions for scoped enum ==/!= int comparisons
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -57,7 +46,7 @@ def test_scoped_enum(capture):
assert z != 3 assert z != 3
def test_implicit_conversion(capture): def test_implicit_conversion():
from pybind11_tests import ExampleWithEnum from pybind11_tests import ExampleWithEnum
assert str(ExampleWithEnum.EMode.EFirstMode) == "EMode.EFirstMode" assert str(ExampleWithEnum.EMode.EFirstMode) == "EMode.EFirstMode"
@ -67,11 +56,8 @@ def test_implicit_conversion(capture):
first = ExampleWithEnum.EFirstMode first = ExampleWithEnum.EFirstMode
second = ExampleWithEnum.ESecondMode second = ExampleWithEnum.ESecondMode
with capture: assert f(first) == 1
f(first)
assert capture == "ExampleWithEnum::test_function(enum=1)"
with capture:
assert f(first) == f(first) assert f(first) == f(first)
assert not f(first) != f(first) assert not f(first) != f(first)
@ -83,48 +69,16 @@ def test_implicit_conversion(capture):
assert f(first) != int(f(second)) assert f(first) != int(f(second))
assert not f(first) == int(f(second)) assert not f(first) == int(f(second))
assert capture == """
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
"""
with capture:
# noinspection PyDictCreation # noinspection PyDictCreation
x = {f(first): 1, f(second): 2} x = {f(first): 1, f(second): 2}
x[f(first)] = 3 x[f(first)] = 3
x[f(second)] = 4 x[f(second)] = 4
assert capture == """
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
ExampleWithEnum::test_function(enum=1)
ExampleWithEnum::test_function(enum=2)
"""
# Hashing test # Hashing test
assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}" assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}"
def test_bytes(capture): def test_bytes():
from pybind11_tests import return_bytes, print_bytes from pybind11_tests import return_bytes, print_bytes
with capture: assert print_bytes(return_bytes()) == "bytes[1 0 2 0]"
print_bytes(return_bytes())
assert capture == """
bytes[0]=1
bytes[1]=0
bytes[2]=2
bytes[3]=0
"""

View File

@ -23,7 +23,7 @@ private:
class Dog : public Pet { class Dog : public Pet {
public: public:
Dog(const std::string &name) : Pet(name, "dog") {} Dog(const std::string &name) : Pet(name, "dog") {}
void bark() const { std::cout << "Woof!" << std::endl; } std::string bark() const { return "Woof!"; }
}; };
class Rabbit : public Pet { class Rabbit : public Pet {
@ -31,12 +31,12 @@ public:
Rabbit(const std::string &name) : Pet(name, "parrot") {} Rabbit(const std::string &name) : Pet(name, "parrot") {}
}; };
void pet_print(const Pet &pet) { std::string pet_name_species(const Pet &pet) {
std::cout << pet.name() + " is a " + pet.species() << std::endl; return pet.name() + " is a " + pet.species();
} }
void dog_bark(const Dog &dog) { std::string dog_bark(const Dog &dog) {
dog.bark(); return dog.bark();
} }
@ -59,7 +59,7 @@ void init_ex_inheritance(py::module &m) {
py::class_<Rabbit>(m, "Rabbit", py::base<Pet>()) py::class_<Rabbit>(m, "Rabbit", py::base<Pet>())
.def(py::init<std::string>()); .def(py::init<std::string>());
m.def("pet_print", pet_print); m.def("pet_name_species", pet_name_species);
m.def("dog_bark", dog_bark); m.def("dog_bark", dog_bark);
py::class_<BaseClass>(m, "BaseClass").def(py::init<>()); py::class_<BaseClass>(m, "BaseClass").def(py::init<>());

View File

@ -34,19 +34,20 @@ void init_issues(py::module &m) {
#endif #endif
// #137: const char* isn't handled properly // #137: const char* isn't handled properly
m2.def("print_cchar", [](const char *string) { std::cout << string << std::endl; }); m2.def("print_cchar", [](const char *s) { return std::string(s); });
// #150: char bindings broken // #150: char bindings broken
m2.def("print_char", [](char c) { std::cout << c << std::endl; }); m2.def("print_char", [](char c) { return std::string(1, c); });
// #159: virtual function dispatch has problems with similar-named functions // #159: virtual function dispatch has problems with similar-named functions
struct Base { virtual void dispatch(void) const { struct Base { virtual std::string dispatch() const {
/* for some reason MSVC2015 can't compile this if the function is pure virtual */ /* for some reason MSVC2015 can't compile this if the function is pure virtual */
return {};
}; }; }; };
struct DispatchIssue : Base { struct DispatchIssue : Base {
virtual void dispatch(void) const { virtual std::string dispatch() const {
PYBIND11_OVERLOAD_PURE(void, Base, dispatch, /* no arguments */); PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */);
} }
}; };
@ -54,7 +55,7 @@ void init_issues(py::module &m) {
.def(py::init<>()) .def(py::init<>())
.def("dispatch", &Base::dispatch); .def("dispatch", &Base::dispatch);
m2.def("dispatch_issue_go", [](const Base * b) { b->dispatch(); }); m2.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
struct Placeholder { int i; Placeholder(int i) : i(i) { } }; struct Placeholder { int i; Placeholder(int i) : i(i) { } };
@ -171,7 +172,7 @@ void init_issues(py::module &m) {
.def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal); .def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal);
py::class_<NestB>(m2, "NestB").def(py::init<>()).def(py::self -= int()).def_readwrite("a", &NestB::a); py::class_<NestB>(m2, "NestB").def(py::init<>()).def(py::self -= int()).def_readwrite("a", &NestB::a);
py::class_<NestC>(m2, "NestC").def(py::init<>()).def(py::self *= int()).def_readwrite("b", &NestC::b); py::class_<NestC>(m2, "NestC").def(py::init<>()).def(py::self *= int()).def_readwrite("b", &NestC::b);
m2.def("print_NestA", [](const NestA &a) { std::cout << a.value << std::endl; }); m2.def("get_NestA", [](const NestA &a) { return a.value; });
m2.def("print_NestB", [](const NestB &b) { std::cout << b.value << std::endl; }); m2.def("get_NestB", [](const NestB &b) { return b.value; });
m2.def("print_NestC", [](const NestC &c) { std::cout << c.value << std::endl; }); m2.def("get_NestC", [](const NestC &c) { return c.value; });
} }

View File

@ -2,24 +2,22 @@ import pytest
import gc import gc
def test_regressions(capture): def test_regressions():
from pybind11_tests.issues import print_cchar, print_char from pybind11_tests.issues import print_cchar, print_char
with capture: # #137: const char* isn't handled properly
print_cchar("const char *") # #137: const char* isn't handled properly assert print_cchar("const char *") == "const char *"
assert capture == "const char *" # #150: char bindings broken
with capture: assert print_char("c") == "c"
print_char("c") # #150: char bindings broken
assert capture == "c"
def test_dispatch_issue(capture, msg): def test_dispatch_issue(msg):
"""#159: virtual function dispatch has problems with similar-named functions""" """#159: virtual function dispatch has problems with similar-named functions"""
from pybind11_tests.issues import DispatchIssue, dispatch_issue_go from pybind11_tests.issues import DispatchIssue, dispatch_issue_go
class PyClass1(DispatchIssue): class PyClass1(DispatchIssue):
def dispatch(self): def dispatch(self):
print("Yay..") return "Yay.."
class PyClass2(DispatchIssue): class PyClass2(DispatchIssue):
def dispatch(self): def dispatch(self):
@ -28,12 +26,10 @@ def test_dispatch_issue(capture, msg):
assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
p = PyClass1() p = PyClass1()
dispatch_issue_go(p) return dispatch_issue_go(p)
b = PyClass2() b = PyClass2()
with capture: assert dispatch_issue_go(b) == "Yay.."
dispatch_issue_go(b)
assert capture == "Yay.."
def test_reference_wrapper(): def test_reference_wrapper():
@ -127,36 +123,26 @@ def test_str_issue(capture, msg):
""" """
def test_nested(capture): def test_nested():
""" #328: first member in a class can't be used in operators""" """ #328: first member in a class can't be used in operators"""
from pybind11_tests.issues import NestA, NestB, NestC, print_NestA, print_NestB, print_NestC from pybind11_tests.issues import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC
a = NestA() a = NestA()
b = NestB() b = NestB()
c = NestC() c = NestC()
a += 10 a += 10
assert get_NestA(a) == 13
b.a += 100 b.a += 100
assert get_NestA(b.a) == 103
c.b.a += 1000 c.b.a += 1000
assert get_NestA(c.b.a) == 1003
b -= 1 b -= 1
assert get_NestB(b) == 3
c.b -= 3 c.b -= 3
assert get_NestB(c.b) == 1
c *= 7 c *= 7
assert get_NestC(c) == 35
with capture:
print_NestA(a)
print_NestA(b.a)
print_NestA(c.b.a)
print_NestB(b)
print_NestB(c.b)
print_NestC(c)
assert capture == """
13
103
1003
3
1
35
"""
abase = a.as_base() abase = a.as_base()
assert abase.value == -2 assert abase.value == -2

View File

@ -10,13 +10,14 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
void kw_func(int x, int y) { std::cout << "kw_func(x=" << x << ", y=" << y << ")" << std::endl; } std::string kw_func(int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }
void kw_func4(const std::vector<int> &entries) { std::string kw_func4(const std::vector<int> &entries) {
std::cout << "kw_func4: "; std::string ret = "{";
for (int i : entries) for (int i : entries)
std::cout << i << " "; ret += std::to_string(i) + " ";
std::cout << endl; ret.back() = '}';
return ret;
} }
py::object call_kw_func(py::function f) { py::object call_kw_func(py::function f) {

View File

@ -5,77 +5,51 @@ from pybind11_tests import (kw_func0, kw_func1, kw_func2, kw_func3, kw_func4, ca
def test_function_signatures(doc): def test_function_signatures(doc):
assert doc(kw_func0) == "kw_func0(arg0: int, arg1: int) -> None" assert doc(kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
assert doc(kw_func1) == "kw_func1(x: int, y: int) -> None" assert doc(kw_func1) == "kw_func1(x: int, y: int) -> str"
assert doc(kw_func2) == "kw_func2(x: int=100, y: int=200) -> None" assert doc(kw_func2) == "kw_func2(x: int=100, y: int=200) -> str"
assert doc(kw_func3) == "kw_func3(data: str='Hello world!') -> None" assert doc(kw_func3) == "kw_func3(data: str='Hello world!') -> None"
assert doc(kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> None" assert doc(kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> str"
assert doc(kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> None" assert doc(kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> str"
assert doc(kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> None" assert doc(kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> str"
assert doc(args_function) == "args_function(*args) -> None" assert doc(args_function) == "args_function(*args) -> None"
assert doc(args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> None" assert doc(args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> None"
assert doc(KWClass.foo0) == "foo0(self: m.KWClass, arg0: int, arg1: float) -> None" assert doc(KWClass.foo0) == "foo0(self: m.KWClass, arg0: int, arg1: float) -> None"
assert doc(KWClass.foo1) == "foo1(self: m.KWClass, x: int, y: float) -> None" assert doc(KWClass.foo1) == "foo1(self: m.KWClass, x: int, y: float) -> None"
def test_named_arguments(capture, msg): def test_named_arguments(msg):
with capture: assert kw_func0(5, 10) == "x=5, y=10"
kw_func1(5, 10)
assert capture == "kw_func(x=5, y=10)"
with capture:
kw_func1(5, y=10)
assert capture == "kw_func(x=5, y=10)"
with capture:
kw_func1(y=10, x=5)
assert capture == "kw_func(x=5, y=10)"
with capture: assert kw_func1(5, 10) == "x=5, y=10"
kw_func2() assert kw_func1(5, y=10) == "x=5, y=10"
assert capture == "kw_func(x=100, y=200)" assert kw_func1(y=10, x=5) == "x=5, y=10"
with capture:
kw_func2(5) assert kw_func2() == "x=100, y=200"
assert capture == "kw_func(x=5, y=200)" assert kw_func2(5) == "x=5, y=200"
with capture: assert kw_func2(x=5) == "x=5, y=200"
kw_func2(x=5) assert kw_func2(y=10) == "x=100, y=10"
assert capture == "kw_func(x=5, y=200)" assert kw_func2(5, 10) == "x=5, y=10"
with capture: assert kw_func2(x=5, y=10) == "x=5, y=10"
kw_func2(y=10)
assert capture == "kw_func(x=100, y=10)"
with capture:
kw_func2(5, 10)
assert capture == "kw_func(x=5, y=10)"
with capture:
kw_func2(x=5, y=10)
assert capture == "kw_func(x=5, y=10)"
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
# noinspection PyArgumentList # noinspection PyArgumentList
kw_func2(x=5, y=10, z=12) kw_func2(x=5, y=10, z=12)
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported: Incompatible function arguments. The following argument types are supported:
1. (x: int=100, y: int=200) -> None 1. (x: int=100, y: int=200) -> str
Invoked with: Invoked with:
""" """
with capture: assert kw_func4() == "{13 17}"
kw_func4() assert kw_func4(myList=[1, 2, 3]) == "{1 2 3}"
assert capture == "kw_func4: 13 17"
with capture:
kw_func4(myList=[1, 2, 3])
assert capture == "kw_func4: 1 2 3"
with capture: assert kw_func_udl(x=5, y=10) == "x=5, y=10"
kw_func_udl(x=5, y=10) assert kw_func_udl_z(x=5) == "x=5, y=0"
assert capture == "kw_func(x=5, y=10)"
with capture:
kw_func_udl_z(x=5)
assert capture == "kw_func(x=5, y=0)"
def test_arg_and_kwargs(capture): def test_arg_and_kwargs(capture):
with capture: assert call_kw_func(kw_func2) == "x=1234, y=5678"
call_kw_func(kw_func2)
assert capture == "kw_func(x=1234, y=5678)"
with capture: with capture:
args_function('arg1_value', 'arg2_value', 3) args_function('arg1_value', 'arg2_value', 3)
assert capture.unordered == """ assert capture.unordered == """

View File

@ -11,8 +11,8 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
void submodule_func() { std::string submodule_func() {
std::cout << "submodule_func()" << std::endl; return "submodule_func()";
} }
class A { class A {

View File

@ -1,14 +1,12 @@
def test_nested_modules(capture): def test_nested_modules():
import pybind11_tests import pybind11_tests
from pybind11_tests.submodule import submodule_func from pybind11_tests.submodule import submodule_func
assert pybind11_tests.__name__ == "pybind11_tests" assert pybind11_tests.__name__ == "pybind11_tests"
assert pybind11_tests.submodule.__name__ == "pybind11_tests.submodule" assert pybind11_tests.submodule.__name__ == "pybind11_tests.submodule"
with capture: assert submodule_func() == "submodule_func()"
submodule_func()
assert capture == "submodule_func()"
def test_reference_internal(): def test_reference_internal():

View File

@ -8,7 +8,6 @@
*/ */
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#ifdef __GNUC__ #ifdef __GNUC__
@ -139,29 +138,48 @@ py::array_t<StringStruct, 0> create_string_array(bool non_empty) {
} }
template <typename S> template <typename S>
void print_recarray(py::array_t<S, 0> arr) { py::list print_recarray(py::array_t<S, 0> arr) {
auto req = arr.request(); const auto req = arr.request();
auto ptr = static_cast<S*>(req.ptr); const auto ptr = static_cast<S*>(req.ptr);
for (size_t i = 0; i < req.size; i++) auto l = py::list();
std::cout << ptr[i] << std::endl; for (size_t i = 0; i < req.size; i++) {
std::stringstream ss;
ss << ptr[i];
l.append(py::str(ss.str()));
}
return l;
} }
void print_format_descriptors() { py::list print_format_descriptors() {
std::cout << py::format_descriptor<SimpleStruct>::format() << std::endl; const auto fmts = {
std::cout << py::format_descriptor<PackedStruct>::format() << std::endl; py::format_descriptor<SimpleStruct>::format(),
std::cout << py::format_descriptor<NestedStruct>::format() << std::endl; py::format_descriptor<PackedStruct>::format(),
std::cout << py::format_descriptor<PartialStruct>::format() << std::endl; py::format_descriptor<NestedStruct>::format(),
std::cout << py::format_descriptor<PartialNestedStruct>::format() << std::endl; py::format_descriptor<PartialStruct>::format(),
std::cout << py::format_descriptor<StringStruct>::format() << std::endl; py::format_descriptor<PartialNestedStruct>::format(),
py::format_descriptor<StringStruct>::format()
};
auto l = py::list();
for (const auto &fmt : fmts) {
l.append(py::cast(fmt));
}
return l;
} }
void print_dtypes() { py::list print_dtypes() {
std::cout << (std::string) py::dtype::of<SimpleStruct>().str() << std::endl; const auto dtypes = {
std::cout << (std::string) py::dtype::of<PackedStruct>().str() << std::endl; py::dtype::of<SimpleStruct>().str(),
std::cout << (std::string) py::dtype::of<NestedStruct>().str() << std::endl; py::dtype::of<PackedStruct>().str(),
std::cout << (std::string) py::dtype::of<PartialStruct>().str() << std::endl; py::dtype::of<NestedStruct>().str(),
std::cout << (std::string) py::dtype::of<PartialNestedStruct>().str() << std::endl; py::dtype::of<PartialStruct>().str(),
std::cout << (std::string) py::dtype::of<StringStruct>().str() << std::endl; py::dtype::of<PartialNestedStruct>().str(),
py::dtype::of<StringStruct>().str()
};
auto l = py::list();
for (const auto &s : dtypes) {
l.append(s);
}
return l;
} }
py::array_t<int32_t, 0> test_array_ctors(int i) { py::array_t<int32_t, 0> test_array_ctors(int i) {

View File

@ -13,39 +13,35 @@ packed_dtype = np.dtype([('x', '?'), ('y', 'u4'), ('z', 'f4')])
@pytest.requires_numpy @pytest.requires_numpy
def test_format_descriptors(capture): def test_format_descriptors():
from pybind11_tests import get_format_unbound, print_format_descriptors from pybind11_tests import get_format_unbound, print_format_descriptors
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
get_format_unbound() get_format_unbound()
assert 'unsupported buffer format' in str(excinfo.value) assert 'unsupported buffer format' in str(excinfo.value)
with capture: assert print_format_descriptors() == [
print_format_descriptors() "T{=?:x:3x=I:y:=f:z:}",
assert capture == """ "T{=?:x:=I:y:=f:z:}",
T{=?:x:3x=I:y:=f:z:} "T{=T{=?:x:3x=I:y:=f:z:}:a:=T{=?:x:=I:y:=f:z:}:b:}",
T{=?:x:=I:y:=f:z:} "T{=?:x:3x=I:y:=f:z:12x}",
T{=T{=?:x:3x=I:y:=f:z:}:a:=T{=?:x:=I:y:=f:z:}:b:} "T{8x=T{=?:x:3x=I:y:=f:z:12x}:a:8x}",
T{=?:x:3x=I:y:=f:z:12x} "T{=3s:a:=3s:b:}"
T{8x=T{=?:x:3x=I:y:=f:z:12x}:a:8x} ]
T{=3s:a:=3s:b:}
"""
@pytest.requires_numpy @pytest.requires_numpy
def test_dtype(capture): def test_dtype():
from pybind11_tests import print_dtypes, test_dtype_ctors, test_dtype_methods from pybind11_tests import print_dtypes, test_dtype_ctors, test_dtype_methods
with capture: assert print_dtypes() == [
print_dtypes() "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}",
assert capture == """ "[('x', '?'), ('y', '<u4'), ('z', '<f4')]",
{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12} "[('a', {'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}), ('b', [('x', '?'), ('y', '<u4'), ('z', '<f4')])]",
[('x', '?'), ('y', '<u4'), ('z', '<f4')] "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}",
[('a', {'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':12}), ('b', [('x', '?'), ('y', '<u4'), ('z', '<f4')])] "{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40}",
{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24} "[('a', 'S3'), ('b', 'S3')]"
{'names':['a'], 'formats':[{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}], 'offsets':[8], 'itemsize':40} ]
[('a', 'S3'), ('b', 'S3')]
"""
d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'], d1 = np.dtype({'names': ['a', 'b'], 'formats': ['int32', 'float64'],
'offsets': [1, 10], 'itemsize': 20}) 'offsets': [1, 10], 'itemsize': 20})
@ -58,7 +54,7 @@ def test_dtype(capture):
@pytest.requires_numpy @pytest.requires_numpy
def test_recarray(capture): def test_recarray():
from pybind11_tests import (create_rec_simple, create_rec_packed, create_rec_nested, from pybind11_tests import (create_rec_simple, create_rec_packed, create_rec_nested,
print_rec_simple, print_rec_packed, print_rec_nested, print_rec_simple, print_rec_packed, print_rec_nested,
create_rec_partial, create_rec_partial_nested) create_rec_partial, create_rec_partial_nested)
@ -77,21 +73,17 @@ def test_recarray(capture):
assert_equal(arr, elements, packed_dtype) assert_equal(arr, elements, packed_dtype)
if dtype == simple_dtype: if dtype == simple_dtype:
with capture: assert print_rec_simple(arr) == [
print_rec_simple(arr) "s:0,0,0",
assert capture == """ "s:1,1,1.5",
s:0,0,0 "s:0,2,3"
s:1,1,1.5 ]
s:0,2,3
"""
else: else:
with capture: assert print_rec_packed(arr) == [
print_rec_packed(arr) "p:0,0,0",
assert capture == """ "p:1,1,1.5",
p:0,0,0 "p:0,2,3"
p:1,1,1.5 ]
p:0,2,3
"""
nested_dtype = np.dtype([('a', simple_dtype), ('b', packed_dtype)]) nested_dtype = np.dtype([('a', simple_dtype), ('b', packed_dtype)])
@ -104,13 +96,11 @@ def test_recarray(capture):
assert_equal(arr, [((False, 0, 0.0), (True, 1, 1.5)), assert_equal(arr, [((False, 0, 0.0), (True, 1, 1.5)),
((True, 1, 1.5), (False, 2, 3.0)), ((True, 1, 1.5), (False, 2, 3.0)),
((False, 2, 3.0), (True, 3, 4.5))], nested_dtype) ((False, 2, 3.0), (True, 3, 4.5))], nested_dtype)
with capture: assert print_rec_nested(arr) == [
print_rec_nested(arr) "n:a=s:0,0,0;b=p:1,1,1.5",
assert capture == """ "n:a=s:1,1,1.5;b=p:0,2,3",
n:a=s:0,0,0;b=p:1,1,1.5 "n:a=s:0,2,3;b=p:1,3,4.5"
n:a=s:1,1,1.5;b=p:0,2,3 ]
n:a=s:0,2,3;b=p:1,3,4.5
"""
arr = create_rec_partial(3) arr = create_rec_partial(3)
assert str(arr.dtype) == "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}" assert str(arr.dtype) == "{'names':['x','y','z'], 'formats':['?','<u4','<f4'], 'offsets':[0,4,8], 'itemsize':24}"
@ -142,19 +132,17 @@ def test_array_constructors():
@pytest.requires_numpy @pytest.requires_numpy
def test_string_array(capture): def test_string_array():
from pybind11_tests import create_string_array, print_string_array from pybind11_tests import create_string_array, print_string_array
arr = create_string_array(True) arr = create_string_array(True)
assert str(arr.dtype) == "[('a', 'S3'), ('b', 'S3')]" assert str(arr.dtype) == "[('a', 'S3'), ('b', 'S3')]"
with capture: assert print_string_array(arr) == [
print_string_array(arr) "a='',b=''",
assert capture == """ "a='a',b='a'",
a='',b='' "a='ab',b='ab'",
a='a',b='a' "a='abc',b='abc'"
a='ab',b='ab' ]
a='abc',b='abc'
"""
dtype = arr.dtype dtype = arr.dtype
assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc'] assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc']
assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc'] assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc']

View File

@ -35,7 +35,7 @@ void init_ex_numpy_vectorize(py::module &m) {
m.def("vectorized_func3", py::vectorize(my_func3)); m.def("vectorized_func3", py::vectorize(my_func3));
/// Numpy function which only accepts specific data types /// Numpy function which only accepts specific data types
m.def("selective_func", [](py::array_t<int, py::array::c_style>) { std::cout << "Int branch taken." << std::endl; }); m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; });
m.def("selective_func", [](py::array_t<float, py::array::c_style>) { std::cout << "Float branch taken." << std::endl; }); m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; });
m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { std::cout << "Complex float branch taken." << std::endl; }); m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; });
} }

View File

@ -59,18 +59,12 @@ def test_vectorize(capture):
@pytest.requires_numpy @pytest.requires_numpy
def test_type_selection(capture): def test_type_selection():
from pybind11_tests import selective_func from pybind11_tests import selective_func
with capture: assert selective_func(np.array([1], dtype=np.int32)) == "Int branch taken."
selective_func(np.array([1], dtype=np.int32)) assert selective_func(np.array([1.0], dtype=np.float32)) == "Float branch taken."
selective_func(np.array([1.0], dtype=np.float32)) assert selective_func(np.array([1.0j], dtype=np.complex64)) == "Complex float branch taken."
selective_func(np.array([1.0j], dtype=np.complex64))
assert capture == """
Int branch taken.
Float branch taken.
Complex float branch taken.
"""
@pytest.requires_numpy @pytest.requires_numpy

View File

@ -38,21 +38,21 @@ void init_ex_opaque_types(py::module &m) {
.def_readwrite("stringList", &ClassWithSTLVecProperty::stringList); .def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
m.def("print_opaque_list", [](const StringList &l) { m.def("print_opaque_list", [](const StringList &l) {
std::cout << "Opaque list: ["; std::string ret = "Opaque list: [";
bool first = true; bool first = true;
for (auto entry : l) { for (auto entry : l) {
if (!first) if (!first)
std::cout << ", "; ret += ", ";
std::cout << entry; ret += entry;
first = false; first = false;
} }
std::cout << "]" << std::endl; return ret + "]";
}); });
m.def("return_void_ptr", []() { return (void *) 0x1234; }); m.def("return_void_ptr", []() { return (void *) 0x1234; });
m.def("print_void_ptr", [](void *ptr) { std::cout << "Got void ptr : 0x" << std::hex << (uint64_t) ptr << std::dec << std::endl; }); m.def("get_void_ptr_value", [](void *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
m.def("return_null_str", []() { return (char *) nullptr; }); m.def("return_null_str", []() { return (char *) nullptr; });
m.def("print_null_str", [](char *ptr) { std::cout << "Got null str : 0x" << std::hex << (uint64_t) ptr << std::dec << std::endl; }); m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast<std::intptr_t>(ptr); });
m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> { m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> {
StringList *result = new StringList(); StringList *result = new StringList();

View File

@ -1,64 +1,48 @@
import pytest import pytest
def test_string_list(capture): def test_string_list():
from pybind11_tests import StringList, ClassWithSTLVecProperty, print_opaque_list from pybind11_tests import StringList, ClassWithSTLVecProperty, print_opaque_list
l = StringList() l = StringList()
l.push_back("Element 1") l.push_back("Element 1")
l.push_back("Element 2") l.push_back("Element 2")
with capture: assert print_opaque_list(l) == "Opaque list: [Element 1, Element 2]"
print_opaque_list(l)
assert capture == "Opaque list: [Element 1, Element 2]"
assert l.back() == "Element 2" assert l.back() == "Element 2"
for i, k in enumerate(l, start=1): for i, k in enumerate(l, start=1):
assert k == "Element {}".format(i) assert k == "Element {}".format(i)
l.pop_back() l.pop_back()
with capture: assert print_opaque_list(l) == "Opaque list: [Element 1]"
print_opaque_list(l)
assert capture == "Opaque list: [Element 1]"
cvp = ClassWithSTLVecProperty() cvp = ClassWithSTLVecProperty()
with capture: assert print_opaque_list(cvp.stringList) == "Opaque list: []"
print_opaque_list(cvp.stringList)
assert capture == "Opaque list: []"
cvp.stringList = l cvp.stringList = l
cvp.stringList.push_back("Element 3") cvp.stringList.push_back("Element 3")
with capture: assert print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"
print_opaque_list(cvp.stringList)
assert capture == "Opaque list: [Element 1, Element 3]"
def test_pointers(capture, msg): def test_pointers(msg):
from pybind11_tests import (return_void_ptr, print_void_ptr, ExampleMandA, from pybind11_tests import (return_void_ptr, get_void_ptr_value, ExampleMandA,
print_opaque_list, return_null_str, print_null_str, print_opaque_list, return_null_str, get_null_str_value,
return_unique_ptr, ConstructorStats) return_unique_ptr, ConstructorStats)
with capture: assert get_void_ptr_value(return_void_ptr()) == 0x1234
print_void_ptr(return_void_ptr()) assert get_void_ptr_value(ExampleMandA()) # Should also work for other C++ types
assert capture == "Got void ptr : 0x1234"
with capture:
print_void_ptr(ExampleMandA()) # Should also work for other C++ types
assert "Got void ptr" in capture
assert ConstructorStats.get(ExampleMandA).alive() == 0 assert ConstructorStats.get(ExampleMandA).alive() == 0
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
print_void_ptr([1, 2, 3]) # This should not work get_void_ptr_value([1, 2, 3]) # This should not work
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported: Incompatible function arguments. The following argument types are supported:
1. (arg0: capsule) -> None 1. (arg0: capsule) -> int
Invoked with: [1, 2, 3] Invoked with: [1, 2, 3]
""" """
assert return_null_str() is None assert return_null_str() is None
with capture: assert get_null_str_value(return_null_str()) is not None
print_null_str(return_null_str())
assert capture == "Got null str : 0x0"
ptr = return_unique_ptr() ptr = return_unique_ptr()
assert "StringList" in repr(ptr) assert "StringList" in repr(ptr)
with capture: assert print_opaque_list(ptr) == "Opaque list: [some value]"
print_opaque_list(ptr)
assert capture == "Opaque list: [some value]"

View File

@ -173,7 +173,7 @@ def test_inheriting_repeat(capture):
assert obj.lucky_number() == -4.25 assert obj.lucky_number() == -4.25
def test_move_support(capture, msg): def test_move_support(capture):
from pybind11_tests import NCVirt, NonCopyable, Movable from pybind11_tests import NCVirt, NonCopyable, Movable
class NCVirtExt(NCVirt): class NCVirtExt(NCVirt):