2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-19 16:32:16 +00:00

Compare commits

...

27 Commits

Author SHA1 Message Date
Stefan Seefeld
32da86df26 Improve test coverage. 2025-12-02 08:53:09 -05:00
Neil Schemenauer
e89f86b74f Update Linux CI scripts, more Python versions.
Update scripts to use actions/setup-python to install different Python
versions.  Add run-faber.sh and get-py-env.py scripts.  Add
test-ubuntu-py-ver.yml CI script to test with different Python versions.
2025-12-02 08:19:45 -05:00
Neil Schemenauer
5013564316 Add "nogil" option for BOOST_PYTHON_MODULE_INIT.
Implement optional arguments for BOOST_PYTHON_MODULE_INIT and allow the
boost::python::mod_gil_not_used() option.  This sets the
Py_MOD_GIL_NOT_USED flag for the extension module.

To define a module that supports free-threaded Python, define it
like this:

    BOOST_PYTHON_MODULE(my_module, boost::python::mod_gil_not_used())
    {
        ...
    }
2025-12-02 08:19:45 -05:00
Neil Schemenauer
fc68878e02 Set the Py_MOD_GIL_NOT_USED flag on modules.
This indicates that the free-threaded build of Python can keep the
GIL disabled when the module is loaded.  Without this module flag,
importing the module will cause the GIL to be re-enabled.  A warning
is emitted if this happens.
2025-11-06 19:46:37 -08:00
Neil Schemenauer
cfbefe893c Use re-entrant mutex to protect global state.
Add pymutex.hpp which implements a re-entrant mutex on top of Python's
PyMutex.  Add BOOST_PYTHON_LOCK_STATE() macro that uses RAII to lock
mutable global state as required.
2025-11-06 19:43:33 -08:00
Neil Schemenauer
6f5f3b6607 Add work-around to crash in ~object_base().
For the free-threaded build (and possibly the debug build), it is not
safe to call Py_DECREF() if there is no valid Python thread-state.
2025-11-06 19:42:41 -08:00
Neil Schemenauer
cabb466057 Use strong reference APIs.
For the free-threaded build, it is not safe use borrowed references.
Another thread could deallocate the object and cause the reference to
become invalid.  Replace API calls that borrow references with strong
reference APIs.
2025-11-06 19:41:00 -08:00
Abhay Kumar
97402f7925 🐛 Fix broken link to Jamroot in example docs 2025-11-03 10:14:34 -05:00
Anton Gladky
668bc7c106 Include missing header boost/type_traits/is_unsigned.hpp
During the Debian Packaging of new version it was found that
this header is missing during the rebuild with GCC-15.
2025-11-03 10:13:40 -05:00
Eisuke Kawashima
5f5f38fa8a fix: fix quotation 2025-11-03 09:30:32 -05:00
Stefan Seefeld
cc873d9682 Fix documentation build error. 2025-11-03 08:40:38 -05:00
Stefan Seefeld
20de46cd0c Update faber 2025-11-03 08:40:38 -05:00
Stefan Seefeld
5d7b9a0648 Stop testing c++98 support 2025-11-03 08:40:38 -05:00
Tom Kent
608ec27c4d Updated to recent compilers/boost 2025-11-03 08:40:38 -05:00
Stefan Seefeld
7fd39323ac Don't rely on Py_REFCNT to test upcast. 2025-11-03 08:40:38 -05:00
Eisuke Kawashima
2b6f667e98 chore: remove meaningless comparison 2025-10-25 15:23:44 -04:00
Eisuke Kawashima
aa458d2ca9 fix(test.properties): use doctest.ELLIPSIS for traceback
Since python 3.11 (PEP 657) traceback info is changed
fix #460
2025-10-25 15:22:21 -04:00
Eisuke Kawashima
16627261f1 fix(test.pickle): fix for change in the return value of object.__reduce__()
https://docs.python.org/3.11/library/pickle.html#object.__reduce__

fix #461
2025-10-25 14:14:07 -04:00
Stefan Seefeld
303299e677 log commands 2025-10-25 13:25:52 -04:00
Stefan Seefeld
cb95b611bb Downgrade run-vcpkg dependency to avoid regression. 2025-10-25 13:25:52 -04:00
Eisuke Kawashima
b4fb28e99a ci: update GitHub Actions 2025-10-23 22:14:55 -04:00
Stefan Seefeld
867f0dddfe Fix windows header path. 2025-10-23 22:08:00 -04:00
Aditya Pillai
a40bb656ee Use Py_REFCNT instead of ob_refcnt on Python 3.9 and above 2025-10-23 19:38:05 -04:00
Eisuke Kawashima
f604eb8d0f fix(test.numpy/ufunc): fix import error and value comparison 2025-04-02 08:21:09 -04:00
Eisuke Kawashima
d30c1bb7a8 refactor: switch to python 3 2025-03-31 21:58:42 -04:00
Aditya Pillai
3e7be69e1e Conditionally use Py_REFCNT 2025-03-10 15:10:12 -04:00
Aditya Pillai
cbdf1ce2a1 Use Py_REFCNT instead of ->ob_refcnt
Py_REFCNT was stabilized in 3.9, uses this official API instead of the
`ob_refcnt` field that doesn't exist in the free-threaded build of 3.13.
2025-03-10 15:10:12 -04:00
51 changed files with 638 additions and 118 deletions

65
.github/get-py-env.py vendored Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
#
# Determine info about the Python install and write shell code to stdout, to
# set env variables. This will set the variables PY_LDFLAGS, PY_CFLAGS and
# PY_INC_PATH.
#
# The python3-config tool is used as the source of this info. In theory we
# could use sysconfig as well but the setup-python action from github appears
# to patch python3-config but not patch the sysconfig info.
#
# Usage:
# eval $(python3 get-py-env.py)
import os
import re
import subprocess
def get_output(cmd):
rv = subprocess.run(
cmd,
capture_output=True, # Capture stdout and stderr
text=True, # Decode output as text (UTF-8)
check=True, # Raise an error if the command fails
)
return rv.stdout
def extract_flags(cmd, prefix):
flags = []
for part in get_output(cmd).split():
part = part.strip()
if part.startswith(prefix):
flags.append(part)
return ' '.join(flags)
def find_python_h():
"""Find the include path that has Python.h contained inside.
We could use INCLUDEPY from sysconfig but github patches
python3-config but not the sysconfig info (after moving the
install).
"""
c_flags = extract_flags(['python3-config', '--cflags'], '-I')
for part in c_flags.split():
m = re.search(r'-I(\S+)', part)
if not m:
continue
inc_path = m.group(1)
if os.path.exists(os.path.join(inc_path, 'Python.h')):
return inc_path
raise SystemExit('cannot find Python.h')
def main():
ld_flags = extract_flags(['python3-config', '--ldflags'], '-L')
c_flags = extract_flags(['python3-config', '--cflags'], '-I')
include_path = find_python_h()
print(f'PY_LDFLAGS="{ld_flags}"')
print(f'PY_CFLAGS="{c_flags}"')
print(f'PY_INC_PATH="{include_path}"')
if __name__ == '__main__':
main()

46
.github/run-faber.sh vendored Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/sh
set -eu
echo "cxx version: $CXX $($CXX --version)"
echo "cxx std: $CXX_STD"
echo "python3 path: $(which python3)"
echo "python3 version: $(python3 --version)"
if ! which faber > /dev/null; then
echo "Installing faber..."
python3 -m pip install --upgrade pip
python3 -m pip install -U faber
fi
echo "faber version: $(faber -v)"
# find and set PY_LDFLAGS and PY_INC_PATH
eval $(python3 .github/get-py-env.py)
echo "PY_INC_PATH=$PY_INC_PATH"
echo "PY_LDFLAGS=$PY_LDFLAGS"
case $(python3-config --abiflags) in
*t*)
# When running with free-threaded, we always want to disable the GIL
# even for extensions without the mod_gil_not_used() flag
export PYTHON_GIL=0
;;
esac
# this could be set by LD_LIBRARY_PATH but faber overrides it
prefix=$(python3-config --prefix)
echo "${prefix}/lib" > /etc/ld.so.conf.d/boost-ci.conf && ldconfig
sed -e "s/\$PYTHON/python3/g" .ci/faber > $HOME/.faber
faber \
--with-boost-include=${BOOST_PY_DEPS} \
--builddir=build \
cxx.name="${CXX}" \
cxxflags="-std=${CXX_STD}" \
cppflags="-std=${CXX_STD}" \
include="${PY_INC_PATH}" \
ldflags="${PY_LDFLAGS}" \
-j`nproc` \
"$@"

View File

@@ -4,14 +4,14 @@ on: [push]
jobs:
deploy:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v5
- name: setup
run: |
sudo apt-get update
sudo apt-get install \
libboost1.71-tools-dev \
libboost-tools-dev \
python3 \
python3-numpy \
python3-sphinx \
@@ -29,7 +29,7 @@ jobs:
echo "destination_dir=doc/develop/html" >> $GITHUB_ENV
fi
- name: deploy
uses: peaceiris/actions-gh-pages@v3
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/doc/html

View File

@@ -14,9 +14,9 @@ jobs:
std: [c++11, c++14] # TODO: c++17 is failing !
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: setup python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: setup prerequisites

View File

@@ -1,6 +1,10 @@
# Test on Ubuntu with various compiler and language standard versions.
name: Test Ubuntu
on: [push, pull_request]
on:
push:
pull_request:
workflow_dispatch:
jobs:
build:
@@ -9,31 +13,59 @@ jobs:
strategy:
fail-fast: false
matrix:
python: [python, python3]
python-version: ['3.14']
cxx: [g++, clang++]
std: [c++98, c++11, c++14, c++17]
std: [c++11, c++14, c++17]
include:
# Add the appropriate docker image for each compiler.
# The images from teeks99/boost-python-test already have boost::python
# pre-reqs installed, see:
# https://github.com/teeks99/boost-python-test-docker
- cxx: clang++
docker-img: teeks99/boost-python-test:clang-12_1.76.0
- cxx: g++
docker-img: teeks99/boost-python-test:gcc-10_1.76.0
- python-version: '2.7'
cxx: g++
std: c++11
- python-version: '3.10'
cxx: g++
std: c++17
- python-version: '3.11'
cxx: g++
std: c++17
- python-version: '3.12'
cxx: g++
std: c++17
- python-version: '3.13'
cxx: g++
std: c++17
# Also test with free-threaded build of Python
- python-version: '3.14t'
cxx: clang++
std: c++17
container:
image: ${{ matrix.docker-img }}
# Add the appropriate docker image for the compiler.
# The images from teeks99/boost-python-test already have boost::python
# pre-reqs installed, see:
# https://github.com/teeks99/boost-python-test-docker
image: ${{ matrix.cxx == 'g++' &&
'teeks99/boost-python-test:gcc-15_1.89.0' ||
'teeks99/boost-python-test:clang-21_1.89.0' }}
steps:
- uses: actions/checkout@v4
- name: build
- uses: actions/checkout@v5
- name: setup python
if: "${{ matrix.python-version != '2.7' }}"
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: setup prerequisites
run: |
${{ matrix.python }} --version
# Warning: this is not necessarily the same Python version as the one configured above !
python3 -m pip install -U faber --break-system-packages
echo "CXX=${{ matrix.cxx }}" >> "$GITHUB_ENV"
echo "CXX_STD=${{ matrix.std }}" >> "$GITHUB_ENV"
- name: build-py2
if: "${{ matrix.python-version == '2.7' }}"
run: |
python --version
${{ matrix.cxx }} --version
faber -v
sed -e "s/\$PYTHON/${{ matrix.python }}/g" .ci/faber > ~/.faber
sed -e "s/\$PYTHON/python/g" .ci/faber > ~/.faber
faber \
--with-boost-include=${BOOST_PY_DEPS} \
--builddir=build \
@@ -41,7 +73,12 @@ jobs:
cxxflags=-std=${{ matrix.std }} \
cppflags=-std=${{ matrix.std }} \
-j`nproc`
- name: test
- name: build-py3
if: "${{ matrix.python-version != '2.7' }}"
run: |
.github/run-faber.sh
- name: test-py2
if: "${{ matrix.python-version == '2.7' }}"
run: |
faber \
--with-boost-include=${BOOST_PY_DEPS} \
@@ -51,3 +88,7 @@ jobs:
cppflags=-std=${{ matrix.std }} \
-j`nproc` \
test.report
- name: test-py3
if: "${{ matrix.python-version != '2.7' }}"
run: |
.github/run-faber.sh test.report

View File

@@ -11,11 +11,11 @@ jobs:
python-version: [3.7]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- uses: microsoft/setup-msbuild@v1.1
- uses: microsoft/setup-msbuild@v2
- name: setup boost prerequisites
uses: lukka/run-vcpkg@v6
with:
@@ -42,8 +42,8 @@ jobs:
- name: build
shell: cmd
run: |
faber --builddir=build cxx.name=msvc --with-boost-include=${{ runner.workspace }}/vcpkg/installed/x64-windows/include -j4
faber --builddir=build cxx.name=msvc --log=commands --log=output --with-boost-include=${{ runner.workspace }}\vcpkg\installed\x64-windows\include -j4
- name: test
shell: cmd
run: |
faber --builddir=build cxx.name=msvc --with-boost-include=${{ runner.workspace }}/vcpkg/installed/x64-windows/include -j4 test.report
faber --builddir=build cxx.name=msvc --with-boost-include=${{ runner.workspace }}\vcpkg\installed\x64-windows\include -j4 test.report

View File

@@ -90,7 +90,7 @@
<tr>
<td valign="top" width="300">
<h3><a href="{{ pathto('index') }}"><img
alt="C++ Boost" src="{{ pathto('_static/' + logo, 1) }}" border="0"></a></h3>
alt="C++ Boost" src="{{ pathto('_static/bpl.png', 1) }}" border="0"></a></h3>
</td>
<td >

View File

@@ -40,8 +40,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Boost.Python NumPy extension'
copyright = u'2011, Stefan Seefeld'
project = 'Boost.Python NumPy extension'
copyright = '2011, Stefan Seefeld'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -181,8 +181,8 @@ html_add_permalinks = False
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'BoostPythonNumPy.tex', u'Boost.Python NumPy Documentation',
u'Stefan Seefeld', 'manual'),
('index', 'BoostPythonNumPy.tex', 'Boost.Python NumPy Documentation',
'Stefan Seefeld', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -214,6 +214,6 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'boostnumpy', u'Boost.Python NumPy Documentation',
[u'Stefan Seefeld'], 1)
('index', 'boostnumpy', 'Boost.Python NumPy Documentation',
['Stefan Seefeld'], 1)
]

View File

@@ -117,7 +117,7 @@ platforms. The complete list of Bjam executables can be found
[h2 Let's Jam!]
__jam__
[@../../../../example/tutorial/Jamroot Here] is our minimalist Jamroot
[@../example/Jamroot Here] is our minimalist Jamroot
file. Simply copy the file and tweak [^use-project boost] to where your
boost root directory is and you're OK.

View File

@@ -3,6 +3,7 @@
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from __future__ import print_function
import numpy
import gaussian
@@ -19,19 +20,19 @@ x, y = numpy.meshgrid(r, r)
z = g(x, y)
s = z.sum() * (r[1] - r[0])**2
print "sum (should be ~ 1):", s
print("sum (should be ~ 1):", s)
xc = (z * x).sum() / z.sum()
print "x centroid (should be ~ %f): %f" % (mu[0], xc)
print("x centroid (should be ~ %f): %f" % (mu[0], xc))
yc = (z * y).sum() / z.sum()
print "y centroid (should be ~ %f): %f" % (mu[1], yc)
print("y centroid (should be ~ %f): %f" % (mu[1], yc))
xx = (z * (x - xc)**2).sum() / z.sum()
print "xx moment (should be ~ %f): %f" % (sigma[0,0], xx)
print("xx moment (should be ~ %f): %f" % (sigma[0,0], xx))
yy = (z * (y - yc)**2).sum() / z.sum()
print "yy moment (should be ~ %f): %f" % (sigma[1,1], yy)
print("yy moment (should be ~ %f): %f" % (sigma[1,1], yy))
xy = 0.5 * (z * (x - xc) * (y - yc)).sum() / z.sum()
print "xy moment (should be ~ %f): %f" % (sigma[0,1], xy)
print("xy moment (should be ~ %f): %f" % (sigma[0,1], xy))

View File

@@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/usr/bin/env python3
# Copyright Stefan Seefeld 2006. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/usr/bin/env python3
# Copyright Ralf W. Grosse-Kunstleve 2006. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/usr/bin/env python3
# Copyright Joel de Guzman 2002-2007. Distributed under the Boost
# Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt
# or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@@ -16,6 +16,7 @@ from faber.config.try_run import try_run
features += include('include')
features += define('BOOST_ALL_NO_LIB') # disable auto-linking
features += define('BOOST_NO_AUTO_PTR')
boost_include = options.get_with('boost-include')
if boost_include:
features += include(boost_include)

View File

@@ -8,6 +8,8 @@
# ifndef BOOST_NO_AUTO_PTR
# include <boost/python/detail/is_xxx.hpp>
# include <memory>
# else
# include <boost/mpl/bool.hpp>
# endif
namespace boost { namespace python { namespace detail {

View File

@@ -0,0 +1,103 @@
// Copyright 2025 Boost.Python Contributors
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PYTHON_DETAIL_PYMUTEX_HPP
#define BOOST_PYTHON_DETAIL_PYMUTEX_HPP
#include <boost/python/detail/prefix.hpp>
#ifdef Py_GIL_DISABLED
// needed for pymutex wrapper
#include <atomic>
#include <cstddef>
#endif
namespace boost { namespace python { namespace detail {
#ifdef Py_GIL_DISABLED
// Re-entrant wrapper around PyMutex for free-threaded Python
// Similar to _PyRecursiveMutex or threading.RLock
class pymutex {
PyMutex m_mutex;
std::atomic<unsigned long> m_owner;
std::size_t m_level;
public:
pymutex() : m_mutex({}), m_owner(0), m_level(0) {}
// Non-copyable, non-movable
pymutex(const pymutex&) = delete;
pymutex& operator=(const pymutex&) = delete;
void lock() {
unsigned long thread = PyThread_get_thread_ident();
if (m_owner.load(std::memory_order_relaxed) == thread) {
m_level++;
return;
}
PyMutex_Lock(&m_mutex);
m_owner.store(thread, std::memory_order_relaxed);
// m_level should be 0 when we acquire the lock
}
void unlock() {
unsigned long thread = PyThread_get_thread_ident();
// Verify current thread owns the lock
if (m_owner.load(std::memory_order_relaxed) != thread) {
// This should never happen - programming error
return;
}
if (m_level > 0) {
m_level--;
return;
}
m_owner.store(0, std::memory_order_relaxed);
PyMutex_Unlock(&m_mutex);
}
bool is_locked_by_current_thread() const {
unsigned long thread = PyThread_get_thread_ident();
return m_owner.load(std::memory_order_relaxed) == thread;
}
};
// RAII lock guard for pymutex
class pymutex_guard {
pymutex& m_mutex;
public:
explicit pymutex_guard(pymutex& mutex) : m_mutex(mutex) {
m_mutex.lock();
}
~pymutex_guard() {
m_mutex.unlock();
}
// Non-copyable, non-movable
pymutex_guard(const pymutex_guard&) = delete;
pymutex_guard& operator=(const pymutex_guard&) = delete;
};
// Global mutex for protecting all Boost.Python internal state
// Similar to pybind11's internals.mutex
BOOST_PYTHON_DECL pymutex& get_global_mutex();
// Macro for acquiring the global lock
// Similar to pybind11's PYBIND11_LOCK_INTERNALS
#define BOOST_PYTHON_LOCK_STATE() \
::boost::python::detail::pymutex_guard lock(::boost::python::detail::get_global_mutex())
#else
// No-op macro when not in free-threaded mode
#define BOOST_PYTHON_LOCK_STATE()
#endif // Py_GIL_DISABLED
}}} // namespace boost::python::detail
#endif // BOOST_PYTHON_DETAIL_PYMUTEX_HPP

View File

@@ -11,11 +11,41 @@
# ifndef BOOST_PYTHON_MODULE_INIT
namespace boost { namespace python { namespace detail {
namespace boost { namespace python {
#ifdef HAS_CXX11
// Use to activate the Py_MOD_GIL_NOT_USED flag.
class mod_gil_not_used {
public:
explicit mod_gil_not_used(bool flag = true) : flag_(flag) {}
bool flag() const { return flag_; }
private:
bool flag_;
};
namespace detail {
inline bool gil_not_used_option() { return false; }
template <typename F, typename... O>
bool gil_not_used_option(F &&, O &&...o);
template <typename... O>
inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) {
return f.flag() || gil_not_used_option(o...);
}
template <typename F, typename... O>
inline bool gil_not_used_option(F &&, O &&...o) {
return gil_not_used_option(o...);
}
}
#endif // HAS_CXX11
namespace detail {
# if PY_VERSION_HEX >= 0x03000000
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)());
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_used = false);
#else
@@ -27,7 +57,37 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
# if PY_VERSION_HEX >= 0x03000000
# define _BOOST_PYTHON_MODULE_INIT(name) \
# ifdef HAS_CXX11
# define _BOOST_PYTHON_MODULE_INIT(name, ...) \
PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
static PyModuleDef_Base initial_m_base = { \
PyObject_HEAD_INIT(NULL) \
0, /* m_init */ \
0, /* m_index */ \
0 /* m_copy */ }; \
static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \
\
static struct PyModuleDef moduledef = { \
initial_m_base, \
BOOST_PP_STRINGIZE(name), \
0, /* m_doc */ \
-1, /* m_size */ \
initial_methods, \
0, /* m_reload */ \
0, /* m_traverse */ \
0, /* m_clear */ \
0, /* m_free */ \
}; \
\
return boost::python::detail::init_module( \
moduledef, BOOST_PP_CAT(init_module_, name), \
boost::python::detail::gil_not_used_option(__VA_ARGS__) ); \
} \
void BOOST_PP_CAT(init_module_, name)()
# else // !HAS_CXX11
# define _BOOST_PYTHON_MODULE_INIT(name) \
PyObject* BOOST_PP_CAT(PyInit_, name)() \
{ \
static PyModuleDef_Base initial_m_base = { \
@@ -53,6 +113,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
moduledef, BOOST_PP_CAT(init_module_, name) ); \
} \
void BOOST_PP_CAT(init_module_, name)()
# endif // HAS_CXX11
# else
@@ -66,9 +127,15 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
# endif
# define BOOST_PYTHON_MODULE_INIT(name) \
# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x03000000)
# define BOOST_PYTHON_MODULE_INIT(name, ...) \
void BOOST_PP_CAT(init_module_,name)(); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name, __VA_ARGS__)
# else
# define BOOST_PYTHON_MODULE_INIT(name) \
void BOOST_PP_CAT(init_module_,name)(); \
extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name)
# endif // HAS_CXX11 && Python 3
# endif

View File

@@ -17,6 +17,7 @@
#include <boost/python/numpy/numpy_object_mgr_traits.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/python/detail/type_traits.hpp>
#include <boost/type_traits/is_unsigned.hpp>
namespace boost { namespace python { namespace numpy {

View File

@@ -419,6 +419,16 @@ inline api::object_base& api::object_base::operator=(api::object_base const& rhs
inline api::object_base::~object_base()
{
#ifdef Py_GIL_DISABLED
// This is a not very elegant fix for a problem that occurs with the
// free-threaded build of Python. If this is called when the interpreter
// has already been finalized, the thread-state can be null. Unlike the
// GIL-enabled build, Py_DECREF() requires a valid thread-state. This
// causes a memory leak, rather than crash, which seems preferable.
if (PyThreadState_GetUnchecked() == NULL) {
return;
}
#endif
assert( Py_REFCNT(m_ptr) > 0 );
Py_DECREF(m_ptr);
}

View File

@@ -216,7 +216,13 @@ namespace boost { namespace python { namespace detail {
{
for (const_iterator i = proxies.begin(); i != proxies.end(); ++i)
{
if ((*i)->ob_refcnt <= 0)
if (
#if PY_VERSION_HEX < 0x03090000
(*i)->ob_refcnt
#else
Py_REFCNT(*i)
#endif
<= 0)
{
PyErr_SetString(PyExc_RuntimeError,
"Invariant: Proxy vector in an inconsistent state");

View File

@@ -11,6 +11,7 @@
#include <boost/python/handle.hpp>
#include <boost/python/detail/raw_pyobject.hpp>
#include <boost/python/detail/pymutex.hpp>
#include <boost/python/cast.hpp>
#include <vector>
@@ -145,6 +146,8 @@ namespace
inline bool visit(rvalue_from_python_chain const* chain)
{
BOOST_PYTHON_LOCK_STATE();
visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
if (p != visited.end() && *p == chain)
return false;
@@ -157,9 +160,11 @@ namespace
{
unvisit(rvalue_from_python_chain const* chain)
: chain(chain) {}
~unvisit()
{
BOOST_PYTHON_LOCK_STATE();
visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
assert(p != visited.end());
visited.erase(p);
@@ -222,7 +227,13 @@ namespace
, char const* ref_type)
{
handle<> holder(source);
if (source->ob_refcnt <= 1)
if (
#if PY_VERSION_HEX < 0x03090000
source->ob_refcnt
#else
Py_REFCNT(source)
#endif
<= 1)
{
handle<> msg(
#if PY_VERSION_HEX >= 0x3000000

View File

@@ -5,6 +5,7 @@
#include <boost/python/converter/registry.hpp>
#include <boost/python/converter/registrations.hpp>
#include <boost/python/converter/builtin_converters.hpp>
#include <boost/python/detail/pymutex.hpp>
#include <set>
#include <stdexcept>
@@ -112,9 +113,9 @@ registration::~registration()
namespace // <unnamed>
{
typedef registration entry;
typedef std::set<entry> registry_t;
#ifndef BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND
registry_t& entries()
{
@@ -181,6 +182,8 @@ namespace // <unnamed>
entry* get(type_info type, bool is_shared_ptr = false)
{
BOOST_PYTHON_LOCK_STATE();
# ifdef BOOST_PYTHON_TRACE_REGISTRY
registry_t::iterator p = entries().find(entry(type));
@@ -293,6 +296,8 @@ namespace registry
registration const* query(type_info type)
{
BOOST_PYTHON_LOCK_STATE();
registry_t::iterator p = entries().find(entry(type));
# ifdef BOOST_PYTHON_TRACE_REGISTRY
std::cout << "querying " << type

View File

@@ -5,6 +5,7 @@
#include <boost/python/type_id.hpp>
#include <boost/python/detail/decorated_type_id.hpp>
#include <boost/python/detail/pymutex.hpp>
#include <utility>
#include <vector>
#include <algorithm>
@@ -81,7 +82,7 @@ namespace
{
free_mem(char*p)
: p(p) {}
~free_mem()
{
std::free(p);
@@ -92,6 +93,7 @@ namespace
bool cxxabi_cxa_demangle_is_broken()
{
BOOST_PYTHON_LOCK_STATE();
static bool was_tested = false;
static bool is_broken = false;
if (!was_tested) {
@@ -109,6 +111,8 @@ namespace detail
{
BOOST_PYTHON_DECL char const* gcc_demangle(char const* mangled)
{
BOOST_PYTHON_LOCK_STATE();
typedef std::vector<
std::pair<char const*, char const*>
> mangling_map;

View File

@@ -68,8 +68,16 @@ object dict_base::get(object_cref k) const
{
if (check_exact(this))
{
#ifdef Py_GIL_DISABLED
PyObject* result;
if (PyDict_GetItemRef(this->ptr(),k.ptr(),&result) < 0) {
throw_error_already_set();
}
return object(detail::new_reference(result ? result : Py_None));
#else
PyObject* result = PyDict_GetItem(this->ptr(),k.ptr());
return object(detail::borrowed_reference(result ? result : Py_None));
#endif
}
else
{

View File

@@ -10,9 +10,21 @@
#include <boost/python/errors.hpp>
#include <boost/cast.hpp>
#include <boost/python/detail/exception_handler.hpp>
#include <boost/python/detail/pymutex.hpp>
namespace boost { namespace python {
#ifdef Py_GIL_DISABLED
namespace detail {
// Global mutex for protecting all Boost.Python internal state
pymutex& get_global_mutex()
{
static pymutex mutex;
return mutex;
}
}
#endif
error_already_set::~error_already_set() {}
// IMPORTANT: this function may only be called from within a catch block!
@@ -20,8 +32,13 @@ BOOST_PYTHON_DECL bool handle_exception_impl(function0<void> f)
{
try
{
if (detail::exception_handler::chain)
return detail::exception_handler::chain->handle(f);
detail::exception_handler* handler_chain = nullptr;
{
BOOST_PYTHON_LOCK_STATE();
handler_chain = detail::exception_handler::chain;
}
if (handler_chain)
return handler_chain->handle(f);
f();
return false;
}
@@ -80,6 +97,7 @@ exception_handler::exception_handler(handler_function const& impl)
: m_impl(impl)
, m_next(0)
{
BOOST_PYTHON_LOCK_STATE();
if (chain != 0)
tail->m_next = this;
else

View File

@@ -38,10 +38,17 @@ BOOST_PYTHON_DECL void scope_setattr_doc(char const* name, object const& x, char
#if PY_VERSION_HEX >= 0x03000000
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef, void(*init_function)())
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef,
void(*init_function)(), bool gil_not_used)
{
PyObject *mod = PyModule_Create(&moduledef);
#ifdef Py_GIL_DISABLED
if (mod != NULL && gil_not_used) {
PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED);
}
#endif
return init_module_in_scope(
PyModule_Create(&moduledef),
mod,
init_function);
}

View File

@@ -161,7 +161,6 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const
else
{
// build a new arg tuple, will adjust its size later
assert(max_arity <= static_cast<std::size_t>(ssize_t_max));
inner_args = handle<>(
PyTuple_New(static_cast<ssize_t>(max_arity)));

View File

@@ -135,7 +135,15 @@ namespace boost { namespace python { namespace objects {
str name(get_qualname(py_type));
if ( py_type->tp_flags & Py_TPFLAGS_HEAPTYPE ) {
// Qualify the type name if it is defined in a different module.
PyObject *type_module_name = PyDict_GetItemString(py_type->tp_dict, "__module__");
PyObject *type_module_name;
#if PY_VERSION_HEX >= 0x030D0000
if (PyDict_GetItemStringRef(py_type->tp_dict, "__module__", &type_module_name) < 0) {
throw_error_already_set();
}
#else
type_module_name = PyDict_GetItemString(py_type->tp_dict, "__module__");
Py_XINCREF(type_module_name);
#endif
if (
type_module_name
&& PyObject_RichCompareBool(
@@ -144,8 +152,11 @@ namespace boost { namespace python { namespace objects {
Py_NE
) != 0
) {
return str("%s.%s" % make_tuple(handle<>(borrowed(type_module_name)), name));
str result = str("%s.%s" % make_tuple(handle<>(type_module_name), name));
return result;
}
// Clean up the strong reference if we didn't use it
Py_XDECREF(type_module_name);
}
return name;
} else {

View File

@@ -4,6 +4,7 @@
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/object/inheritance.hpp>
#include <boost/python/type_id.hpp>
#include <boost/python/detail/pymutex.hpp>
#include <boost/graph/breadth_first_search.hpp>
#if _MSC_FULL_VER >= 13102171 && _MSC_FULL_VER <= 13102179
# include <boost/graph/reverse_graph.hpp>
@@ -390,6 +391,8 @@ namespace
inline void* convert_type(void* const p, class_id src_t, class_id dst_t, bool polymorphic)
{
BOOST_PYTHON_LOCK_STATE();
// Quickly rule out unregistered types
index_entry* src_p = seek_type(src_t);
if (src_p == 0)
@@ -452,6 +455,8 @@ BOOST_PYTHON_DECL void* find_static_type(void* p, class_id src_t, class_id dst_t
BOOST_PYTHON_DECL void add_cast(
class_id src_t, class_id dst_t, cast_function cast, bool is_downcast)
{
BOOST_PYTHON_LOCK_STATE();
// adding an edge will invalidate any record of unreachability in
// the cache.
static std::size_t expected_cache_len = 0;
@@ -490,6 +495,7 @@ BOOST_PYTHON_DECL void add_cast(
BOOST_PYTHON_DECL void register_dynamic_id_aux(
class_id static_id, dynamic_id_function get_dynamic_id)
{
BOOST_PYTHON_LOCK_STATE();
tuples::get<kdynamic_id>(*demand_type(static_id)) = get_dynamic_id;
}

View File

@@ -21,20 +21,28 @@ namespace detail
this->m_self, const_cast<char*>(name))))
)
{
PyObject* borrowed_f = 0;
PyObject* class_f = 0;
if (
PyMethod_Check(m.get())
&& PyMethod_GET_SELF(m.get()) == this->m_self
&& class_object->tp_dict != 0
)
{
borrowed_f = ::PyDict_GetItemString(
#if PY_VERSION_HEX >= 0x030D0000
if (::PyDict_GetItemStringRef(
class_object->tp_dict, const_cast<char*>(name), &class_f) < 0) {
throw_error_already_set();
}
#else
class_f = ::PyDict_GetItemString(
class_object->tp_dict, const_cast<char*>(name));
Py_XINCREF(class_f);
#endif
}
if (borrowed_f != PyMethod_GET_FUNCTION(m.get()))
bool is_override = (class_f != PyMethod_GET_FUNCTION(m.get()));
Py_XDECREF(class_f);
if (is_override)
return override(m);
}
}

View File

@@ -99,7 +99,7 @@ BOOST_PYTHON_MODULE(back_reference_ext)
.def("set", &Y::set)
;
class_<Z,std::auto_ptr<Z> >("Z", init<int>())
class_<Z,std::shared_ptr<Z> >("Z", init<int>())
.def("value", &Z::value)
.def("set", &Z::set)
;

View File

@@ -9,14 +9,13 @@
struct foo
{
operator std::auto_ptr<int>&() const;
operator std::shared_ptr<int>&() const;
};
int main()
{
using namespace boost::python::detail;
BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs<int>::value);
BOOST_STATIC_ASSERT(copy_ctor_mutates_rhs<std::auto_ptr<int> >::value);
BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs<std::string>::value);
BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs<foo>::value);
return 0;

View File

@@ -68,6 +68,7 @@ for t in [('injected',),
('raw_ctor',),
('exception_translator',),
('module_init_exception',),
('module_nogil',),
('test_enum', ['enum_ext']),
('test_cltree', ['cltree']),
('newtest', ['m1', 'm2']),
@@ -118,10 +119,10 @@ for t in [('injected',),
tests.append(extension_test('shared_ptr',
condition=set.define.contains('HAS_CXX11')))
tests.append(extension_test('polymorphism2_auto_ptr',
condition=set.define.contains('HAS_CXX11').not_()))
tests.append(extension_test('auto_ptr',
condition=set.define.contains('HAS_CXX11')))
#tests.append(extension_test('polymorphism2_auto_ptr',
# condition=set.define.contains('HAS_CXX11').not_()))
#tests.append(extension_test('auto_ptr',
# condition=set.define.contains('HAS_CXX11')))
import_ = binary('import_', ['import_.cpp', src.bpl], features=features|python_libs)
if platform.os == 'Windows':

View File

@@ -17,7 +17,7 @@ typedef test_class<> X;
X* empty() { return new X(1000); }
std::auto_ptr<X> sum(int a, int b) { return std::auto_ptr<X>(new X(a+b)); }
std::shared_ptr<X> sum(int a, int b) { return std::shared_ptr<X>(new X(a+b)); }
boost::shared_ptr<X> product(int a, int b, int c)
{

25
test/module_nogil.cpp Normal file
View File

@@ -0,0 +1,25 @@
// Test for BOOST_PYTHON_MODULE with optional mod_gil_not_used argument
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
// Simple function to export
int get_value() {
return 1234;
}
#if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x03000000)
// C++11 build with Python 3: test with mod_gil_not_used option
BOOST_PYTHON_MODULE(module_nogil_ext, boost::python::mod_gil_not_used())
{
using namespace boost::python;
def("get_value", get_value);
}
#else
// C++98 build or Python 2: test without optional arguments
BOOST_PYTHON_MODULE(module_nogil_ext)
{
using namespace boost::python;
def("get_value", get_value);
}
#endif

29
test/module_nogil.py Normal file
View File

@@ -0,0 +1,29 @@
"""
>>> from module_nogil_ext import *
>>> get_value()
1234
>>> import sys, sysconfig
>>> Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
>>> if Py_GIL_DISABLED and sys._is_gil_enabled():
... print('GIL is enabled and should not be')
... else:
... print('okay')
okay
"""
from __future__ import print_function
def run(args = None):
import sys
import doctest
if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))
if __name__ == '__main__':
print("running...")
import sys
status = run()[0]
if (status == 0): print("Done.")
sys.exit(status)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.
@@ -15,7 +15,7 @@ if (sys.version_info.major >= 3):
class DtypeTestCase(unittest.TestCase):
def assertEquivalent(self, a, b):
return self.assert_(dtype_ext.equivalent(a, b), "%r is not equivalent to %r")
return self.assertTrue(dtype_ext.equivalent(a, b), "%r is not equivalent to %r")
def testIntegers(self):
for bits in (8, 16, 32, 64):

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.
@@ -19,7 +19,7 @@ class TestNdarray(unittest.TestCase):
a1 = ndarray_ext.zeros(shape,dt)
a2 = v.reshape(a1.shape)
self.assertEqual(shape,a1.shape)
self.assert_((a1 == a2).all())
self.assertTrue((a1 == a2).all())
def testNdzeros_matrix(self):
for dtp in (numpy.int16, numpy.int32, numpy.float32, numpy.complex128):
@@ -28,7 +28,7 @@ class TestNdarray(unittest.TestCase):
a1 = ndarray_ext.zeros_matrix(shape, dt)
a2 = numpy.matrix(numpy.zeros(shape, dtype=dtp))
self.assertEqual(shape,a1.shape)
self.assert_((a1 == a2).all())
self.assertTrue((a1 == a2).all())
self.assertEqual(type(a1), type(a2))
def testNdarray(self):
@@ -38,8 +38,8 @@ class TestNdarray(unittest.TestCase):
dt = numpy.dtype(dtp)
a1 = ndarray_ext.array(a)
a2 = ndarray_ext.array(a,dt)
self.assert_((a1 == v).all())
self.assert_((a2 == v).all())
self.assertTrue((a1 == v).all())
self.assertTrue((a2 == v).all())
for shape in ((60,),(6,10),(4,3,5),(2,2,3,5)):
a1 = a1.reshape(shape)
self.assertEqual(shape,a1.shape)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.
@@ -18,7 +18,7 @@ class TestTemplates(unittest.TestCase):
a1 = numpy.zeros(shape, dtype=dtype)
a2 = v.reshape(a1.shape)
templates_ext.fill(a1)
self.assert_((a1 == a2).all())
self.assertTrue((a1 == a2).all())
a1 = numpy.zeros((12,), dtype=numpy.float64)
self.assertRaises(TypeError, templates_ext.fill, a1)
a1 = numpy.zeros((12,2,3), dtype=numpy.float32)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright Jim Bosch & Ankit Daftery 2010-2012.
# Distributed under the Boost Software License, Version 1.0.
@@ -8,7 +8,10 @@
import ufunc_ext
import unittest
import numpy
from numpy.testing.utils import assert_array_almost_equal
try:
from numpy.testing import assert_array_almost_equal
except ImportError:
from numpy.testing.utils import assert_array_almost_equal
class TestUnary(unittest.TestCase):
@@ -24,7 +27,7 @@ class TestUnary(unittest.TestCase):
assert_array_almost_equal(b, a*2.0)
c = numpy.zeros(5, dtype=float)
d = f(a,output=c)
self.assert_(c is d)
self.assertTrue((c == d).all())
assert_array_almost_equal(d, a*2.0)
def testList(self):
@@ -47,7 +50,7 @@ class TestBinary(unittest.TestCase):
assert_array_almost_equal(f(a,b), (a*2+b*3))
c = numpy.zeros(5, dtype=float)
d = f(a,b,output=c)
self.assert_(c is d)
self.assertTrue((c == d).all())
assert_array_almost_equal(d, a*2 + b*3)
assert_array_almost_equal(f(a, 2.0), a*2 + 6.0)
assert_array_almost_equal(f(1.0, b), 2.0 + b*3)

View File

@@ -36,7 +36,7 @@ BOOST_PYTHON_MODULE( operators_wrapper_ext )
;
scope().attr("v") = vector();
std::auto_ptr<vector> dp(new dvector);
register_ptr_to_python< std::auto_ptr<vector> >();
std::shared_ptr<vector> dp(new dvector);
register_ptr_to_python< std::shared_ptr<vector> >();
scope().attr("d") = dp;
}

View File

@@ -9,8 +9,10 @@ r'''>>> import pickle1_ext
1
>>> pickle1_ext.world.__name__
'world'
>>> pickle1_ext.world('Hello').__reduce__()
>>> pickle1_ext.world('Hello').__reduce__() # doctest: +PY310
(<class 'pickle1_ext.world'>, ('Hello',))
>>> pickle1_ext.world('Hello').__reduce__() # doctest: +PY311
(<class 'pickle1_ext.world'>, ('Hello',), None)
>>> wd = pickle1_ext.world('California')
>>> pstr = pickle.dumps(wd)
>>> wl = pickle.loads(pstr)
@@ -31,7 +33,27 @@ def run(args = None):
if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))
# > https://docs.python.org/3.11/library/pickle.html#object.__reduce__
# object.__reduce__() returns
# - python 3.10 or prior: a 2-element tuple
# - python 3.11 or later: a 3-element tuple (object's state added)
PY310 = doctest.register_optionflag("PY310")
PY311 = doctest.register_optionflag("PY311")
class ConditionalChecker(doctest.OutputChecker):
def check_output(self, want, got, optionflags):
if (optionflags & PY311) and (sys.version_info[:2] < (3, 11)):
return True
if (optionflags & PY310) and (sys.version_info[:2] >= (3, 11)):
return True
return doctest.OutputChecker.check_output(self, want, got, optionflags)
runner = doctest.DocTestRunner(ConditionalChecker())
for test in doctest.DocTestFinder().find(sys.modules.get(__name__)):
runner.run(test)
return doctest.TestResults(runner.failures, runner.tries)
if __name__ == '__main__':
print("running...")

View File

@@ -12,8 +12,10 @@ r'''>>> import pickle4_ext
1
>>> pickle4_ext.world.__name__
'world'
>>> pickle4_ext.world('Hello').__reduce__()
>>> pickle4_ext.world('Hello').__reduce__() # doctest: +PY310
(<class 'pickle4_ext.world'>, ('Hello',))
>>> pickle4_ext.world('Hello').__reduce__() # doctest: +PY311
(<class 'pickle4_ext.world'>, ('Hello',), None)
>>> wd = pickle4_ext.world('California')
>>> pstr = pickle.dumps(wd)
>>> wl = pickle.loads(pstr)
@@ -29,7 +31,27 @@ def run(args = None):
if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))
# > https://docs.python.org/3.11/library/pickle.html#object.__reduce__
# object.__reduce__() returns
# - python 3.10 or prior: a 2-element tuple
# - python 3.11 or later: a 3-element tuple (object's state added)
PY310 = doctest.register_optionflag("PY310")
PY311 = doctest.register_optionflag("PY311")
class ConditionalChecker(doctest.OutputChecker):
def check_output(self, want, got, optionflags):
if (optionflags & PY311) and (sys.version_info[:2] < (3, 11)):
return True
if (optionflags & PY310) and (sys.version_info[:2] >= (3, 11)):
return True
return doctest.OutputChecker.check_output(self, want, got, optionflags)
runner = doctest.DocTestRunner(ConditionalChecker())
for test in doctest.DocTestFinder().find(sys.modules.get(__name__)):
runner.run(test)
return doctest.TestResults(runner.failures, runner.tries)
if __name__ == '__main__':
print("running...")

View File

@@ -56,11 +56,10 @@ class instance count from object:
1
as expected you can't assign new value to read only property
>>> x1.value_r = 2
>>> x1.value_r = 2 # doctest: +ELLIPSIS
Traceback (most recent call last):
File "properties.py", line 49, in ?
x1.value_r = 2
AttributeError: can't set attribute
...
AttributeError: ...
setting value_rw to 2. value_direct:
>>> x1.value_rw = 2

View File

@@ -62,14 +62,14 @@ int test_main(int, char * [])
assert_holder<Base,Derived
,value_holder_back_reference<Base,Derived> >();
assert_holder<Base,std::auto_ptr<Base>
,pointer_holder<std::auto_ptr<Base>,Base> >();
assert_holder<Base,std::unique_ptr<Base>
,pointer_holder<std::unique_ptr<Base>,Base> >();
assert_holder<Base,std::auto_ptr<Derived>
,pointer_holder_back_reference<std::auto_ptr<Derived>,Base> >();
assert_holder<Base,std::unique_ptr<Derived>
,pointer_holder_back_reference<std::unique_ptr<Derived>,Base> >();
assert_holder<BR,std::auto_ptr<BR>
,pointer_holder_back_reference<std::auto_ptr<BR>,BR> > ();
assert_holder<BR,std::unique_ptr<BR>
,pointer_holder_back_reference<std::unique_ptr<BR>,BR> > ();
return 0;
}

View File

@@ -38,7 +38,7 @@
12
>>> try: modify(p)
... except TypeError: pass
... else: 'print(expected a TypeError)'
... else: print('expected a TypeError')
>>> look(None)
-1
>>> store(p)
@@ -61,7 +61,7 @@ bye
13
>>> try: modify(z)
... except TypeError: pass
... else: 'print(expected a TypeError)'
... else: print('expected a TypeError')
>>> Z.get() # should be None
>>> store(z)
@@ -84,7 +84,7 @@ bye
17
>>> try: modify(x)
... except TypeError: pass
... else: 'print(expected a TypeError)'
... else: print('expected a TypeError')
>>> look(None)
-1
>>> store(x)

View File

@@ -1,7 +1,7 @@
# Copyright David Abrahams 2004. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#!/usr/bin/env python
#!/usr/bin/env python3
from cltree import basic,symbol,constant,variable

View File

@@ -13,7 +13,7 @@ int main()
{
PyTypeObject o;
Y y;
BOOST_TEST(&Py_REFCNT(boost::python::upcast<PyObject>(&o)) == &Py_REFCNT(&o));
BOOST_TEST(&Py_REFCNT(boost::python::upcast<PyObject>(&y)) == &Py_REFCNT(&y));
BOOST_TEST(boost::python::upcast<PyObject>(&o) == reinterpret_cast<PyObject*>(&o));
BOOST_TEST(boost::python::upcast<PyObject>(&y) == &y);
return boost::report_errors();
}

View File

@@ -20,12 +20,12 @@ struct data
}
};
std::auto_ptr<data> create_data()
std::shared_ptr<data> create_data()
{
return std::auto_ptr<data>( new data );
return std::shared_ptr<data>( new data );
}
void do_nothing( std::auto_ptr<data>& ){}
void do_nothing( std::shared_ptr<data>& ){}
namespace bp = boost::python;
@@ -59,7 +59,7 @@ struct data_wrapper : data, bp::wrapper< data >
BOOST_PYTHON_MODULE(wrapper_held_type_ext)
{
bp::class_< data_wrapper, std::auto_ptr< data > >( "data" )
bp::class_< data_wrapper, std::shared_ptr< data > >( "data" )
.def( "id", &data::id, &::data_wrapper::default_id );
bp::def( "do_nothing", &do_nothing );