Skip to content

Commit 228d3ef

Browse files
author
opennao
committed
Merge commit 'refs/integration/team/yoshi/dev-75'
2 parents ec9fc0e + d92be88 commit 228d3ef

38 files changed

Lines changed: 776 additions & 348 deletions

cmake/install_runtime_dependencies.cmake

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ if(_unresolved_runtime_dependencies)
5858
message(STATUS "Some dependencies of qi_python and qi are unresolved: ${_unresolved_runtime_dependencies}")
5959
endif()
6060

61+
if(UNIX OR APPLE)
62+
find_program(_patchelf "patchelf")
63+
if(_patchelf)
64+
if(APPLE)
65+
set(_patchelf_runtime_path "@loader_path")
66+
elseif(UNIX)
67+
set(_patchelf_runtime_path "$ORIGIN")
68+
endif()
69+
else()
70+
message(WARNING "The target system seems to be using ELF binaries, but the `patchelf` \
71+
tool could not be found. The installed runtime dependencies might not have their runtime path \
72+
set correctly to run. You might want to install `patchelf` on your system.")
73+
endif()
74+
endif()
75+
6176
foreach(_needed_dep IN LISTS _runtime_dependencies)
6277
set(_dep "${_needed_dep}")
6378

@@ -80,11 +95,19 @@ foreach(_needed_dep IN LISTS _runtime_dependencies)
8095
endwhile()
8196

8297
file(INSTALL "${_dep}" DESTINATION "${_module_install_dir}")
98+
get_filename_component(_dep_filename "${_dep}" NAME)
99+
set(_installed_dep "${_module_install_dir}/${_dep_filename}")
100+
101+
if(_patchelf)
102+
message(STATUS "Set runtime path of runtime dependency \"${_installed_dep}\" to \"${_patchelf_runtime_path}\"")
103+
file(TO_NATIVE_PATH "${_installed_dep}" _native_installed_dep)
104+
execute_process(COMMAND "${_patchelf}"
105+
# Sets the RPATH/RUNPATH or may replace the RPATH by a RUNPATH, depending on the platform.
106+
--set-rpath "${_patchelf_runtime_path}" "${_native_installed_dep}")
107+
endif()
83108

84109
if(NOT "${_dep}" STREQUAL "${_needed_dep}")
85-
get_filename_component(_dep_filename "${_dep}" NAME)
86110
get_filename_component(_needed_dep_filename "${_needed_dep}" NAME)
87-
set(_installed_dep "${_module_install_dir}/${_dep_filename}")
88111
set(_installed_needed_dep "${_module_install_dir}/${_needed_dep_filename}")
89112
message(STATUS "Renaming ${_installed_dep} into ${_installed_needed_dep}")
90113
file(RENAME "${_installed_dep}" "${_installed_needed_dep}")

cmake/set_dependencies.cmake

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@
4545
include_guard(GLOBAL)
4646

4747
# Version of Boost to use. It will be used as the argument to the `find_package(Boost)` call.
48-
overridable_variable(BOOST_VERSION 1.64)
48+
overridable_variable(BOOST_VERSION 1.77)
4949

5050
# Version of pybind11 to use.
51-
overridable_variable(PYBIND11_VERSION 2.5.0)
51+
overridable_variable(PYBIND11_VERSION 2.9.0)
5252

5353
# URL of the git repository from which to download pybind11. For more details, see CMake
5454
# `ExternalProject` module documentation of the `GIT_REPOSITORY` argument.
@@ -58,16 +58,13 @@ overridable_variable(PYBIND11_GIT_REPOSITORY https://github.com/pybind/pybind11)
5858
# `ExternalProject` module documentation of the `GIT_TAG` argument.
5959
overridable_variable(PYBIND11_GIT_TAG v${PYBIND11_VERSION})
6060

61-
# Version of googletest to use.
62-
overridable_variable(GOOGLETEST_VERSION 1.10.0)
63-
6461
# URL of the git repository from which to download googletest. For more details, see CMake
6562
# `ExternalProject` module documentation of the `GIT_REPOSITORY` argument.
6663
overridable_variable(GOOGLETEST_GIT_REPOSITORY https://github.com/google/googletest.git)
6764

6865
# Git branch name, tag or commit hash to checkout for googletest. For more details, see CMake
6966
# `ExternalProject` module documentation of the `GIT_TAG` argument.
70-
overridable_variable(GOOGLETEST_GIT_TAG release-${GOOGLETEST_VERSION})
67+
overridable_variable(GOOGLETEST_GIT_TAG main)
7168

7269
set(PYTHON_VERSION_STRING "" CACHE STRING "Version of Python to look for. This \
7370
variable can be specified by tools run directly from Python to enforce the \

cmake/set_libqi_dependency.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
overridable_variable(LIBQI_VERSION 2.0.0)
1+
overridable_variable(LIBQI_VERSION 2.1.0)
22

33
# Our github clone is sometimes late or is missing tags, so we enable
44
# customisation of the URL at configuration time, so users can use another clone.

examples/authentication_with_application.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,35 +28,60 @@ def newAuthenticator(self):
2828

2929

3030
# Reads a file containing the username on the first line and the password on
31-
# the second line.
31+
# the second line. This is the format used by qilaunch.
3232
def read_auth_file(path):
3333
with open(path) as f:
3434
username = f.readline().strip()
3535
password = f.readline().strip()
3636
return (username, password)
3737

3838

39-
def make_application():
40-
app = qi.Application(sys.argv)
41-
parser = argparse.ArgumentParser()
39+
def make_application(argv=sys.argv):
40+
"""
41+
Create and return the qi.Application, with authentication set up
42+
according to the command line options.
43+
"""
44+
# create the app and edit `argv` in place to remove the consumed
45+
# arguments.
46+
# As a side effect, if "-h" is in the list, it is replaced with "--help".
47+
app = qi.Application(argv)
48+
49+
# Setup a non-intrusive parser, behaving like `qi.Application`'s own
50+
# parser:
51+
# * don't complain about unknown arguments
52+
# * consume known arguments
53+
# * if the "--help" option is present:
54+
# * print its own options help
55+
# * do not print the main app usage
56+
# * do not call `sys.exit()`
57+
parser = argparse.ArgumentParser(add_help=False, usage=argparse.SUPPRESS)
4258
parser.add_argument(
4359
"-a", "--authfile",
4460
help="Path to the authentication config file. This file must "
4561
"contain the username on the first line and the password on the "
4662
"second line.")
47-
args = parser.parse_args(sys.argv[1:])
63+
if "--help" in argv:
64+
parser.print_help()
65+
return app
66+
args, unparsed_args = parser.parse_known_args(argv[1:])
4867
logins = read_auth_file(args.authfile) if args.authfile else ("nao", "nao")
4968
factory = AuthenticatorFactory(*logins)
5069
app.session.setClientAuthenticatorFactory(factory)
70+
# edit argv in place.
71+
# Note: this might modify sys.argv, like qi.Application does.
72+
argv[1:] = unparsed_args
5173
return app
5274

5375

5476
if __name__ == "__main__":
77+
parser = argparse.ArgumentParser()
78+
parser.add_argument("--msg", default="Hello python")
5579
app = make_application()
80+
args = parser.parse_args()
5681
logger = qi.Logger("authentication_with_application")
5782
logger.info("connecting session")
5883
app.start()
5984
logger.info("fetching ALTextToSpeech service")
6085
tts = app.session.service("ALTextToSpeech")
6186
logger.info("Saying something")
62-
tts.call("say", "Hello python")
87+
tts.call("say", args.msg)

pyproject.toml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
[project]
2+
dependencies = [
3+
"packaging"
4+
]
5+
6+
# PEP 518
17
[build-system]
2-
requires = ["setuptools>=47.1", "wheel>=0.34", "scikit-build>=0.10.1",
3-
"cmake>=3.17", "ninja>=1.5"]
8+
# Minimum requirements for the build system to execute.
9+
requires = [
10+
"setuptools >= 47, < 51", # setuptools 51 dropped support for Python 3.5.
11+
"wheel >= 0.34", # tested with 0.34.
12+
"scikit-build >= 0.10", # tested with 0.10.
13+
"cmake ~= 3.16", # CMake >= 3.16, CMake < 4.
14+
"ninja ~= 1", # ninja >= 1, ninja < 2.
15+
"toml ~= 10",
16+
"packaging ~= 21",
17+
]

qi/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
"""LibQi Python bindings."""
77

88
import platform
9-
py_version = platform.python_version_tuple()
10-
if py_version < ('3', '5'):
11-
raise RuntimeError('Python version 3.5+ is required.')
9+
from packaging import version
10+
11+
py_version = version.parse(platform.python_version())
12+
min_version = version.parse('3.5')
13+
if py_version < min_version:
14+
raise RuntimeError('Python 3.5+ is required.')
1215

1316
import sys # noqa: E402
1417
import atexit # noqa: E402

qi/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.0.0'
1+
__version__ = '3.1.0'

qi/test/test_module.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,34 @@ def test_module_service():
4141

4242
cat = session.service("Cat")
4343
assert cat.meow(3) == 'meow'
44+
45+
46+
def test_module_service_object_lifetime():
47+
session = qi.Session()
48+
session.listenStandalone("tcp://localhost:0")
49+
session.loadServiceRename("moduletest.Cat", "", "truc")
50+
cat = session.service("Cat")
51+
52+
# We use `del` to release the reference to an object, but there is no
53+
# guarantee that the interpreter will finalize the object right away.
54+
# In CPython, this is "mostly" guaranteed as long as it is the last
55+
# reference to the object and that there is no cyclic dependency of
56+
# reference anywhere that might hold the reference.
57+
# As there is no practical alternative, we consider that assuming the
58+
# object is finalized immediately is an acceptable hypothesis.
59+
60+
# Purr has a property, Play has a signal, Sleep has none.
61+
sleep = cat.makeSleep()
62+
assert cat.nbSleep() == 1
63+
del sleep
64+
assert cat.nbSleep() == 0
65+
66+
purr = cat.makePurr()
67+
assert cat.nbPurr() == 1
68+
del purr
69+
assert cat.nbPurr() == 0
70+
71+
play = cat.makePlay()
72+
assert cat.nbPlay() == 1
73+
del play
74+
assert cat.nbPlay() == 0

qipython/common.hpp

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@
1919
#include <future>
2020
#include <thread>
2121

22+
23+
// MAJOR, MINOR and PATCH must be in [0, 255].
24+
#define QI_PYBIND11_VERSION(MAJOR, MINOR, PATCH) \
25+
(((MAJOR) << 16) | \
26+
((MINOR) << 8) | \
27+
((PATCH) << 0))
28+
29+
#ifdef PYBIND11_VERSION_HEX
30+
// Remove the lowest 8 bits which represent the serial and level version components.
31+
# define QI_CURRENT_PYBIND11_VERSION (PYBIND11_VERSION_HEX >> 8)
32+
#else
33+
# define QI_CURRENT_PYBIND11_VERSION \
34+
QI_PYBIND11_VERSION(PYBIND11_VERSION_MAJOR, \
35+
PYBIND11_VERSION_MINOR, \
36+
PYBIND11_VERSION_PATCH)
37+
#endif
38+
2239
namespace qi
2340
{
2441
namespace py
@@ -93,32 +110,20 @@ boost::optional<T> extractKeywordArg(pybind11::dict kwargs,
93110
return pyArg.cast<T>();
94111
}
95112

96-
/// A deleter that deletes the pointer outside of the GIL.
97-
///
98-
/// Useful for types that might deadlock on destruction if they keep the GIL
99-
/// locked.
100-
struct DeleteOutsideGIL
101-
{
102-
template<typename T>
103-
void operator()(T* ptr) const
104-
{
105-
pybind11::gil_scoped_release unlock;
106-
delete ptr;
107-
}
108-
};
109-
110-
/// Delay the destruction of an object to a thread.
111-
struct DeleteInOtherThread
113+
/// Returns whether or not the interpreter is finalizing. If the information
114+
/// could not be fetched, the function returns an empty optional. Otherwise, it
115+
/// returns an optional set with a boolean stating if the interpreter is indeed
116+
/// finalizing or not.
117+
inline boost::optional<bool> interpreterIsFinalizing()
112118
{
113-
template<typename T>
114-
void operator()(T* ptr) const
115-
{
116-
pybind11::gil_scoped_release unlock;
117-
auto fut = std::async(std::launch::async, [](std::unique_ptr<T>) {},
118-
std::unique_ptr<T>(ptr));
119-
QI_IGNORE_UNUSED(fut);
120-
}
121-
};
119+
// `_Py_IsFinalizing` is only available on CPython 3.7+
120+
#if PY_VERSION_HEX >= 0x03070000
121+
return boost::make_optional(_Py_IsFinalizing() != 0);
122+
#else
123+
// There is no way of knowing on older versions.
124+
return {};
125+
#endif
126+
}
122127

123128
} // namespace py
124129
} // namespace qi

qipython/pyfuture.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ using Promise = qi::Promise<AnyValue>;
2525
// future or its value.
2626
inline pybind11::object resultObject(const Future& fut, bool async)
2727
{
28-
pybind11::gil_scoped_acquire lock;
28+
GILAcquire lock;
2929
if (async)
3030
return castToPyObject(fut);
3131

3232
// Wait for the future outside of the GIL.
33-
auto res = invokeGuarded<pybind11::gil_scoped_release>(qi::SrcFuture{}, fut);
33+
auto res = invokeGuarded<GILRelease>(qi::SrcFuture{}, fut);
3434
return castToPyObject(res);
3535
}
3636

0 commit comments

Comments
 (0)