diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 714a51bde7..0000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,104 +0,0 @@ -environment: - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: http://stackoverflow.com/a/13751649/163740 - CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\run_with_env.cmd" - BOOST_PREFIX: C:\Libraries\boost_1_63_0 - - matrix: - - # Pre-installed Python versions, which Appveyor may upgrade to - # a later point release. - # See: http://www.appveyor.com/docs/installed-software#python - - - PYTHON: "C:\\Python27" - PYTHON_VERSION: "2.7.x" # currently 2.7.9 - PYTHON_ARCH: "32" - MSVC: "14.0" - ARCH: x86 - - #- PYTHON: "C:\\Python27-x64" - # PYTHON_VERSION: "2.7.x" # currently 2.7.9 - # PYTHON_ARCH: "64" - # ARCH: "x86_64" - - #- PYTHON: "C:\\Python35" - # PYTHON_VERSION: "3.5.x" # currently 3.4.3 - # PYTHON_ARCH: "32" - - - PYTHON: "C:\\Python36-x64" - PYTHON_VERSION: "3.6.x" - PYTHON_ARCH: "64" - MSVC: "12.0" - ARCH: x86_64 - -install: - # If there is a newer build queued for the same PR, cancel this one. - # The AppVeyor 'rollout builds' option is supposed to serve the same - # purpose but it is problematic because it tends to cancel builds pushed - # directly to master instead of just PR builds (or the converse). - # credits: JuliaLang developers. - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } - - ECHO "Filesystem root:" - - ps: "ls \"C:/\"" - - ECHO "Installed libraries:" - - ps: "ls \"C:/Libraries/\"" - - ECHO "Installed SDKs:" - - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" - - # Install Python (from the official .msi of http://python.org) and pip when - # not already installed. - - ps: if (-not(Test-Path($env:PYTHON))) { & .ci\install.ps1 } - - # Prepend newly installed Python to the PATH of this build (this cannot be - # done from inside the powershell script as it would require to restart - # the parent CMD process). - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - # Check that we have the expected version and architecture for Python - - "python --version" - - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - - # Upgrade to the latest version of pip to avoid it displaying warnings - # about it being out of date. - - "pip install --disable-pip-version-check --user --upgrade pip" - - # Install the build dependencies of the project. If some dependencies contain - # compiled extensions and are not provided as pre-built wheel packages, - # pip will build them from source using the MSVC compiler matching the - # target Python version and architecture - - | - curl -LfsS -o faber.tar.gz https://github.com/stefanseefeld/faber/archive/release/0.2.tar.gz - tar xf faber.tar.gz - CD faber-release-0.2 - python setup.py install - CD .. - # report the available MSVC compilers - - python -m faber.tools.msvc - - easy_install sphinx - - pip install numpy - -build_script: - - faber --with-boost-include=%BOOST_PREFIX% target.arch=%ARCH% msvc.version=%MSVC% - -test_script: - - faber --with-boost-include=%BOOST_PREFIX% test.report target.arch=%ARCH% msvc.version=%MSVC% - -after_test: - # If tests are successful, create binary packages for the project. - #- "%CMD_IN_ENV% python setup.py bdist_wheel" - #- "%CMD_IN_ENV% python setup.py bdist_wininst" - #- "%CMD_IN_ENV% python setup.py bdist_msi" - #- ps: "ls dist" - -#artifacts: - # Archive the generated packages in the ci.appveyor.com build report. - #- path: dist\* - -#on_success: -# - TODO: upload the content of dist/*.whl to a public wheelhouse -# diff --git a/.ci/faber b/.ci/faber index b23e8a18ba..ddb36ddf66 100644 --- a/.ci/faber +++ b/.ci/faber @@ -1,3 +1,7 @@ +# -*- python -*- + +from faber.tools.boost import boostbook from faber.tools.python import python +bb = boostbook(prefix='/usr/share/boostbook') p = python(command='$PYTHON') diff --git a/.ci/install.ps1 b/.ci/install.ps1 deleted file mode 100644 index 94d6f01813..0000000000 --- a/.ci/install.ps1 +++ /dev/null @@ -1,229 +0,0 @@ -# Sample script to install Python and pip under Windows -# Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer -# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ - -$MINICONDA_URL = "http://repo.continuum.io/miniconda/" -$BASE_URL = "https://www.python.org/ftp/python/" -$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -$GET_PIP_PATH = "C:\get-pip.py" - -$PYTHON_PRERELEASE_REGEX = @" -(?x) -(?\d+) -\. -(?\d+) -\. -(?\d+) -(?[a-z]{1,2}\d+) -"@ - - -function Download ($filename, $url) { - $webclient = New-Object System.Net.WebClient - - $basedir = $pwd.Path + "\" - $filepath = $basedir + $filename - if (Test-Path $filename) { - Write-Host "Reusing" $filepath - return $filepath - } - - # Download and retry up to 3 times in case of network transient errors. - Write-Host "Downloading" $filename "from" $url - $retry_attempts = 2 - for ($i = 0; $i -lt $retry_attempts; $i++) { - try { - $webclient.DownloadFile($url, $filepath) - break - } - Catch [Exception]{ - Start-Sleep 1 - } - } - if (Test-Path $filepath) { - Write-Host "File saved at" $filepath - } else { - # Retry once to get the error message if any at the last try - $webclient.DownloadFile($url, $filepath) - } - return $filepath -} - - -function ParsePythonVersion ($python_version) { - if ($python_version -match $PYTHON_PRERELEASE_REGEX) { - return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, - $matches.prerelease) - } - $version_obj = [version]$python_version - return ($version_obj.major, $version_obj.minor, $version_obj.build, "") -} - - -function DownloadPython ($python_version, $platform_suffix) { - $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version - - if (($major -le 2 -and $micro -eq 0) ` - -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` - ) { - $dir = "$major.$minor" - $python_version = "$major.$minor$prerelease" - } else { - $dir = "$major.$minor.$micro" - } - - if ($prerelease) { - if (($major -le 2) ` - -or ($major -eq 3 -and $minor -eq 1) ` - -or ($major -eq 3 -and $minor -eq 2) ` - -or ($major -eq 3 -and $minor -eq 3) ` - ) { - $dir = "$dir/prev" - } - } - - if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { - $ext = "msi" - if ($platform_suffix) { - $platform_suffix = ".$platform_suffix" - } - } else { - $ext = "exe" - if ($platform_suffix) { - $platform_suffix = "-$platform_suffix" - } - } - - $filename = "python-$python_version$platform_suffix.$ext" - $url = "$BASE_URL$dir/$filename" - $filepath = Download $filename $url - return $filepath -} - - -function InstallPython ($python_version, $architecture, $python_home) { - Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home - if (Test-Path $python_home) { - Write-Host $python_home "already exists, skipping." - return $false - } - if ($architecture -eq "32") { - $platform_suffix = "" - } else { - $platform_suffix = "amd64" - } - $installer_path = DownloadPython $python_version $platform_suffix - $installer_ext = [System.IO.Path]::GetExtension($installer_path) - Write-Host "Installing $installer_path to $python_home" - $install_log = $python_home + ".log" - if ($installer_ext -eq '.msi') { - InstallPythonMSI $installer_path $python_home $install_log - } else { - InstallPythonEXE $installer_path $python_home $install_log - } - if (Test-Path $python_home) { - Write-Host "Python $python_version ($architecture) installation complete" - } else { - Write-Host "Failed to install Python in $python_home" - Get-Content -Path $install_log - Exit 1 - } -} - - -function InstallPythonEXE ($exepath, $python_home, $install_log) { - $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" - RunCommand $exepath $install_args -} - - -function InstallPythonMSI ($msipath, $python_home, $install_log) { - $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" - $uninstall_args = "/qn /x $msipath" - RunCommand "msiexec.exe" $install_args - if (-not(Test-Path $python_home)) { - Write-Host "Python seems to be installed else-where, reinstalling." - RunCommand "msiexec.exe" $uninstall_args - RunCommand "msiexec.exe" $install_args - } -} - -function RunCommand ($command, $command_args) { - Write-Host $command $command_args - Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru -} - - -function InstallPip ($python_home) { - $pip_path = $python_home + "\Scripts\pip.exe" - $python_path = $python_home + "\python.exe" - if (-not(Test-Path $pip_path)) { - Write-Host "Installing pip..." - $webclient = New-Object System.Net.WebClient - $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) - Write-Host "Executing:" $python_path $GET_PIP_PATH - & $python_path $GET_PIP_PATH - } else { - Write-Host "pip already installed." - } -} - - -function DownloadMiniconda ($python_version, $platform_suffix) { - if ($python_version -eq "3.4") { - $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" - } else { - $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" - } - $url = $MINICONDA_URL + $filename - $filepath = Download $filename $url - return $filepath -} - - -function InstallMiniconda ($python_version, $architecture, $python_home) { - Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home - if (Test-Path $python_home) { - Write-Host $python_home "already exists, skipping." - return $false - } - if ($architecture -eq "32") { - $platform_suffix = "x86" - } else { - $platform_suffix = "x86_64" - } - $filepath = DownloadMiniconda $python_version $platform_suffix - Write-Host "Installing" $filepath "to" $python_home - $install_log = $python_home + ".log" - $args = "/S /D=$python_home" - Write-Host $filepath $args - Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru - if (Test-Path $python_home) { - Write-Host "Python $python_version ($architecture) installation complete" - } else { - Write-Host "Failed to install Python in $python_home" - Get-Content -Path $install_log - Exit 1 - } -} - - -function InstallMinicondaPip ($python_home) { - $pip_path = $python_home + "\Scripts\pip.exe" - $conda_path = $python_home + "\Scripts\conda.exe" - if (-not(Test-Path $pip_path)) { - Write-Host "Installing pip..." - $args = "install --yes pip" - Write-Host $conda_path $args - Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru - } else { - Write-Host "pip already installed." - } -} - -function main () { - InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON - InstallPip $env:PYTHON -} - -main \ No newline at end of file diff --git a/.ci/run_with_env.cmd b/.ci/run_with_env.cmd deleted file mode 100644 index 5da547c499..0000000000 --- a/.ci/run_with_env.cmd +++ /dev/null @@ -1,88 +0,0 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment -:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) -:: -:: To build extensions for 64 bit Python 2, we need to configure environment -:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) -:: -:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific -:: environment configurations. -:: -:: Note: this script needs to be run with the /E:ON and /V:ON flags for the -:: cmd interpreter, at least for (SDK v7.0) -:: -:: More details at: -:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows -:: http://stackoverflow.com/a/13751649/163740 -:: -:: Author: Olivier Grisel -:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ -:: -:: Notes about batch files for Python people: -:: -:: Quotes in values are literally part of the values: -:: SET FOO="bar" -:: FOO is now five characters long: " b a r " -:: If you don't want quotes, don't include them on the right-hand side. -:: -:: The CALL lines at the end of this file look redundant, but if you move them -:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y -:: case, I don't know why. -@ECHO OFF - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf - -:: Extract the major and minor versions, and allow for the minor version to be -:: more than 9. This requires the version number to have two dots in it. -SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% -IF "%PYTHON_VERSION:~3,1%" == "." ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% -) ELSE ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% -) - -:: Based on the Python version, determine what SDK version to use, and whether -:: to set the SDK for 64-bit. -IF %MAJOR_PYTHON_VERSION% == 2 ( - SET WINDOWS_SDK_VERSION="v7.0" - SET SET_SDK_64=Y -) ELSE ( - IF %MAJOR_PYTHON_VERSION% == 3 ( - SET WINDOWS_SDK_VERSION="v7.1" - IF %MINOR_PYTHON_VERSION% LEQ 4 ( - SET SET_SDK_64=Y - ) ELSE ( - SET SET_SDK_64=N - IF EXIST "%WIN_WDK%" ( - :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN "%WIN_WDK%" 0wdf - ) - ) - ) ELSE ( - ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" - EXIT 1 - ) -) - -IF %PYTHON_ARCH% == 64 ( - IF %SET_SDK_64% == Y ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) ELSE ( - ECHO Using default MSVC build environment for 64 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) -) ELSE ( - ECHO Using default MSVC build environment for 32 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) diff --git a/.ci/upload_docs.sh b/.ci/upload_docs.sh deleted file mode 100755 index aa20838d5c..0000000000 --- a/.ci/upload_docs.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -set -e # Exit with nonzero exit code if anything fails - -SOURCE_BRANCH="master" -TARGET_BRANCH="gh-pages" - -# Pull requests and commits to other branches shouldn't try to deploy, just build to verify -if [ "$TRAVIS_PULL_REQUEST" != "false" ] || \ - [ "$TRAVIS_BRANCH" != master -a \ - "$TRAVIS_BRANCH" != develop -a \ - "$TRAVIS_BRANCH" != travis ]; then - echo "No docs to upload." - exit 0 -fi - -if [ -z "$GH_TOKEN" ]; then - echo "Error: GH_TOKEN is undefined" - exit 1 -fi - -# Save some useful information -REPO=`git config remote.origin.url` -SHA=`git rev-parse --verify HEAD` - -# bin.SCons happens to contain the "doc/html" tree that we want to push -# into the gh-pages branch. So we step into that directory, create a new repo, -# set the remote appropriately, then commit and push. -cd bin.SCons -git init -git config user.name "Travis CI" -git config user.email "travis-ci" - -# Make sure 'GH_TOKEN' is set (as 'secure' variable) in .travis.yml -git remote add upstream "https://$GH_TOKEN@github.com/boostorg/python.git" -git fetch upstream -git reset upstream/gh-pages - -# Prepare version. -if [ "$TRAVIS_BRANCH" = develop -o "$TRAVIS_BRANCH" = travis ]; then - mkdir -p develop/doc/ - cp ../index.html develop/ - cp ../doc/index.html develop/doc/ - cp -a doc/html develop/doc/ - git add develop/index.html - git add develop/doc/index.html - git add -A develop/doc/html -else - cp ../index.html . - cp ../doc/index.html doc/ - git add index.html - git add doc/index.html - git add -A doc/html -fi -# Commit the new version. -git commit -m "Deploy to GitHub Pages: ${SHA}" - -# Now that we're all set up, we can push. -git push -q upstream HEAD:gh-pages diff --git a/.github/get-py-env.py b/.github/get-py-env.py new file mode 100755 index 0000000000..a6c41460d8 --- /dev/null +++ b/.github/get-py-env.py @@ -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() diff --git a/.github/run-faber.sh b/.github/run-faber.sh new file mode 100755 index 0000000000..5cb78be6dd --- /dev/null +++ b/.github/run-faber.sh @@ -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` \ + "$@" diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml new file mode 100644 index 0000000000..3c5ffafb70 --- /dev/null +++ b/.github/workflows/deploy-documentation.yml @@ -0,0 +1,37 @@ +name: deploy documentation + +on: [push] + +jobs: + deploy: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - name: setup + run: | + sudo apt-get update + sudo apt-get install \ + libboost-tools-dev \ + python3 \ + python3-numpy \ + python3-sphinx \ + xsltproc \ + docbook-xsl + sudo python3 -m pip install --upgrade pip + sudo python3 -m pip install faber + - name: build + run: | + sed -e "s/\$PYTHON/python3/g" .ci/faber > ~/.faber + faber --builddir=build doc.html + if [ "${GITHUB_REF##*/}" == master ]; then + echo "destination_dir=doc/html" >> $GITHUB_ENV + else + echo "destination_dir=doc/develop/html" >> $GITHUB_ENV + fi + - name: deploy + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: build/doc/html + destination_dir: ${{ env.destination_dir }} + keep_files: true diff --git a/.github/workflows/test-osx.yml b/.github/workflows/test-osx.yml new file mode 100644 index 0000000000..b88b43e5dd --- /dev/null +++ b/.github/workflows/test-osx.yml @@ -0,0 +1,50 @@ +name: Test OSX + +on: [push, pull_request] + +jobs: + build: + runs-on: macOS-latest + + strategy: + fail-fast: false + matrix: + python-version: [3.8.10] + cxx: [clang++] + std: [c++11, c++14] # TODO: c++17 is failing ! + + steps: + - uses: actions/checkout@v5 + - name: setup python + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + - name: setup prerequisites + run: | + brew install boost + python -m pip install --upgrade pip + python -m pip install setuptools faber + - name: build + run: | + python --version + ${{ matrix.cxx }} --version + brew info boost + faber -v + sed -e "s/\$PYTHON/python/g" .ci/faber > ~/.faber + faber \ + --with-boost-include=$(brew --prefix boost)/include \ + --builddir=build \ + cxx.name=${{ matrix.cxx }} \ + cxxflags=-std=${{ matrix.std }} \ + cppflags=-std=${{ matrix.std }} \ + -j`sysctl -n hw.ncpu` + - name: test + run: | + faber \ + --with-boost-include=$(brew --prefix boost)/include \ + --builddir=build\ + cxx.name=${{ matrix.cxx }} \ + cxxflags=-std=${{ matrix.std }} \ + cppflags=-std=${{ matrix.std }} \ + -j`sysctl -n hw.ncpu` \ + test.report diff --git a/.github/workflows/test-ubuntu.yml b/.github/workflows/test-ubuntu.yml new file mode 100644 index 0000000000..31637de5ef --- /dev/null +++ b/.github/workflows/test-ubuntu.yml @@ -0,0 +1,94 @@ +# Test on Ubuntu with various compiler and language standard versions. +name: Test Ubuntu + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + python-version: ['3.14'] + cxx: [g++, clang++] + std: [c++11, c++14, c++17] + include: + - 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: + # 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@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: | + # 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/python/g" .ci/faber > ~/.faber + faber \ + --with-boost-include=${BOOST_PY_DEPS} \ + --builddir=build \ + cxx.name=${{ matrix.cxx }} \ + cxxflags=-std=${{ matrix.std }} \ + cppflags=-std=${{ matrix.std }} \ + -j`nproc` + - 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} \ + --builddir=build \ + cxx.name=${{ matrix.cxx }} \ + cxxflags=-std=${{ matrix.std }} \ + cppflags=-std=${{ matrix.std }} \ + -j`nproc` \ + test.report + - name: test-py3 + if: "${{ matrix.python-version != '2.7' }}" + run: | + .github/run-faber.sh test.report diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml new file mode 100644 index 0000000000..576e1f4415 --- /dev/null +++ b/.github/workflows/test-windows.yml @@ -0,0 +1,49 @@ +name: Test Windows + +on: [push, pull_request] + +jobs: + build: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + python-version: [3.7] + + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + - uses: microsoft/setup-msbuild@v2 + - name: setup boost prerequisites + uses: lukka/run-vcpkg@v6 + with: + vcpkgGitCommitId: '88b1071e39f13b632644d9d953738d345a4ac055' + vcpkgDirectory: '${{ runner.workspace }}/vcpkg' + vcpkgTriplet: x64-windows + vcpkgArguments: > + boost-config + boost-core + boost-function + boost-graph + boost-iterator + boost-lexical-cast + boost-mpl + boost-preprocessor + boost-smart-ptr + boost-static-assert + boost-align + - name: setup faber + run: | + python -m pip install --upgrade pip + python -m pip install setuptools faber numpy + faber --info=tools cxx + - name: build + shell: cmd + run: | + 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 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e93421930a..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -sudo: required -dist: trusty - -language: cpp - -env: - global: - - secure: BRNUkxN3p8f+uYKWC3Hr0VPqZA0PxbWr1DJlcI4hbiZtzKhMCWjDmd9UW9CzzexqeOxpd+9s0G87qvOur+wMSVxugDxtTesZrh1czXHeSVxgQrYD783XJtQJ9aYypbChkiboRD6Xpmbq7itwMuHBJMFtCuDxMynpU1jWwkyTf2Y= - -matrix: - include: - - compiler: gcc - env: CXX=g++ PYTHON=python CXXFLAGS=-std=c++98 - - compiler: gcc - env: CXX=g++ PYTHON=python CXXFLAGS=-std=c++11 - - compiler: gcc - env: CXX=g++ PYTHON=python3 CXXFLAGS=-std=c++98 - - compiler: gcc - env: CXX=g++ PYTHON=python3 CXXFLAGS=-std=c++11 - - compiler: clang - env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++98 - - compiler: clang - env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++11 - #- env: PYTHON=python DOC=1 - - -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - scons - - gcc-4.8 - - g++-4.8 - - clang - - python-numpy - - python-sphinx - - python3-dev - - python3-numpy - - libboost-all-dev - - xsltproc - - docbook-xsl - - python-docutils - - -cache: - directories: - - $HOME/Boost - -before_install: - # The Trusty image has several Python versions pre-installed compiled with - # conflicting UCS2 and UCS4 unicode. Modify the PATH to skip the TravisCI python. - # See https://github.com/travis-ci/travis-ci/issues/4948 for details. - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - -install: - # Install our own version of Boost (the subset we need) as the system version is - # too old (for C++11 support). - - rm -rf $HOME/Boost - - | - set -e - if [ ! -d $HOME/Boost ]; then - echo "rebuilding Boost prerequisites" - wget https://sourceforge.net/projects/boost/files/boost/1.61.0/boost_1_61_0.tar.gz/download - tar xf download - pushd boost_1_61_0 - ./bootstrap.sh - ./b2 tools/bcp - mkdir -p $HOME/Boost - # Install Boost.Python prerequisites, but not Boost.Python itself. - dist/bin/bcp python tools/boostbook tools/quickbook $HOME/Boost &> /dev/null - rm -rf $HOME/Boost/boost/python* - popd - # Install Faber, the build tool. - date=2017-11-09 - #wget https://github.com/stefanseefeld/faber/archive/snapshot/$date.tar.gz - wget https://github.com/stefanseefeld/faber/archive/release/0.2.tar.gz - tar xf 0.2.tar.gz - pushd faber-release-0.2 - sudo python setup.py install - popd - fi - -before_script: -- sed -e "s/\$PYTHON/$PYTHON/g" .ci/faber > ~/.faber -- $PYTHON --version -- faber -h - -script: -- faber --with-boost-include=$HOME/Boost test.report cxx.name=$CXX cxxflags=$CXXFLAGS -#- if [ "$DOC" ]; then scons doc; else scons && scons test; fi - -after_success: -# Upload docs only when building upstream. -- | - if [ "$DOC" -a \ - "$TRAVIS_REPO_SLUG" = "boostorg/python" -a \ - "$TRAVIS_PULL_REQUEST" = "false" ]; then - export GH_TOKEN - .ci/upload_docs.sh - fi diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..299ef84e9c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,176 @@ +# Copyright 2020, 2021 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.14...3.20) + +project(boost_python VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) + +find_package(Python REQUIRED COMPONENTS Development OPTIONAL_COMPONENTS NumPy) + +if(Python_NumPy_FOUND) + message(STATUS "Boost.Python: using Python ${Python_VERSION} with NumPy at ${Python_NumPy_INCLUDE_DIRS}") +else() + message(STATUS "Boost.Python: using Python ${Python_VERSION} without NumPy") +endif() + +# boost_pythonXY + +set(_pyver ${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) +set(_boost_python boost_python${_pyver}) + +add_library(${_boost_python} + src/dict.cpp + src/errors.cpp + src/exec.cpp + src/import.cpp + src/list.cpp + src/long.cpp + src/module.cpp + src/object_operators.cpp + src/object_protocol.cpp + src/slice.cpp + src/str.cpp + src/tuple.cpp + src/wrapper.cpp + src/converter/from_python.cpp + src/converter/registry.cpp + src/converter/type_id.cpp + src/converter/builtin_converters.cpp + src/converter/arg_to_python_base.cpp + src/object/enum.cpp + src/object/class.cpp + src/object/function.cpp + src/object/inheritance.cpp + src/object/life_support.cpp + src/object/pickle_support.cpp + src/object/iterator.cpp + src/object/stl_iterator.cpp + src/object_protocol.cpp + src/object_operators.cpp + src/object/function_doc_signature.cpp +) + +add_library(Boost::python${_pyver} ALIAS ${_boost_python}) + +target_include_directories(${_boost_python} PUBLIC include) + +target_link_libraries(${_boost_python} + PUBLIC + Boost::align + Boost::bind + Boost::config + Boost::conversion + Boost::core + Boost::detail + Boost::foreach + Boost::function + Boost::iterator + Boost::lexical_cast + Boost::mpl + Boost::numeric_conversion + Boost::preprocessor + Boost::smart_ptr + Boost::static_assert + Boost::tuple + Boost::type_traits + Boost::utility + + Python::Module + + PRIVATE + Boost::graph + Boost::integer + Boost::property_map +) + +target_compile_definitions(${_boost_python} + PUBLIC BOOST_PYTHON_NO_LIB + PRIVATE BOOST_PYTHON_SOURCE +) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(${_boost_python} PUBLIC BOOST_PYTHON_DYN_LINK) +else() + target_compile_definitions(${_boost_python} PUBLIC BOOST_PYTHON_STATIC_LINK BOOST_PYTHON_STATIC_LIB) +endif() + +# Boost::python alias + +add_library(boost_python INTERFACE) +add_library(Boost::python ALIAS boost_python) +target_link_libraries(boost_python INTERFACE Boost::python${_pyver}) + +# Installation + +if(BOOST_SUPERPROJECT_VERSION AND NOT CMAKE_VERSION VERSION_LESS 3.13) + boost_install(TARGETS ${_boost_python} boost_python VERSION ${BOOST_SUPERPROJECT_VERSION} HEADER_DIRECTORY include) +endif() + +if(Python_NumPy_FOUND) + +# boost_numpyXY + +set(_boost_numpy boost_numpy${_pyver}) + +add_library(${_boost_numpy} + src/numpy/dtype.cpp + src/numpy/matrix.cpp + src/numpy/ndarray.cpp + src/numpy/numpy.cpp + src/numpy/scalars.cpp + src/numpy/ufunc.cpp +) + +add_library(Boost::numpy${_pyver} ALIAS ${_boost_numpy}) + +target_include_directories(${_boost_numpy} PUBLIC include) + +target_link_libraries(${_boost_numpy} + PUBLIC + Boost::config + Boost::core + Boost::detail + Boost::mpl + Boost::python + Boost::smart_ptr + + Python::NumPy +) + +target_compile_definitions(${_boost_numpy} + PUBLIC BOOST_NUMPY_NO_LIB + PRIVATE BOOST_NUMPY_SOURCE +) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(${_boost_numpy} PUBLIC BOOST_NUMPY_DYN_LINK) +else() + target_compile_definitions(${_boost_numpy} PUBLIC BOOST_NUMPY_STATIC_LINK BOOST_NUMPY_STATIC_LIB) +endif() + +# Boost::numpy alias + +add_library(boost_numpy INTERFACE) +add_library(Boost::numpy ALIAS boost_numpy) +target_link_libraries(boost_numpy INTERFACE Boost::numpy${_pyver}) + +# Installation + +if(BOOST_SUPERPROJECT_VERSION AND NOT CMAKE_VERSION VERSION_LESS 3.13) + boost_install(TARGETS ${_boost_numpy} boost_numpy VERSION ${BOOST_SUPERPROJECT_VERSION}) +endif() + +endif() + +unset(_pyver) +unset(_boost_python) +unset(_boost_numpy) + +# Testing + +if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") + + add_subdirectory(test) + +endif() diff --git a/README.md b/README.md index 373d4e874a..f57b97505a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # Synopsis +[![Join the chat at https://gitter.im/boostorg/python](https://badges.gitter.im/boostorg/python.svg)](https://gitter.im/boostorg/python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + Welcome to Boost.Python, a C++ library which enables seamless interoperability between C++ and the Python programming language. The library includes support for: * References and Pointers @@ -19,7 +21,7 @@ See the [Boost.Python](http://boostorg.github.io/python) documentation for detai **Hint :** Check out the [development version](http://boostorg.github.io/python/develop) of the documentation to see work in progress. -# Building ![Build Status](https://travis-ci.org/boostorg/python.svg?branch=develop) +# Building ![Test Ubuntu](https://github.com/boostorg/python/workflows/Test%20Ubuntu/badge.svg) ![Test OSX](https://github.com/boostorg/python/workflows/Test%20OSX/badge.svg) ![Test Windows](https://github.com/boostorg/python/workflows/Test%20Windows/badge.svg) While Boost.Python is part of the Boost C++ Libraries super-project, and thus can be compiled as part of Boost, it can also be compiled and installed stand-alone, i.e. against a pre-installed Boost package. @@ -27,27 +29,14 @@ While Boost.Python is part of the Boost C++ Libraries super-project, and thus ca * [Python](http://www.python.org) * [Boost](http://www.boost.org) -* [SCons](http://www.scons.org) - -## Configure - -Simply run - -``` -scons config [options] -``` -to prepare a build. See `scons -h` for a description of the available options. For example -``` -scons config --boost=/path/to/boost --python=/path/to/python -``` -will configure Boost.Python to be built against the two specific versions of Boost and Python. +* [Faber](https://stefanseefeld.github.io/faber) ## Build Run ``` -scons +faber ``` to build the library. @@ -56,7 +45,7 @@ to build the library. Run ``` -scons test +faber test.report ``` to run the tests. @@ -65,6 +54,6 @@ to run the tests. Run ``` -scons doc +faber doc.html ``` to build the documentation. diff --git a/SConstruct b/SConstruct deleted file mode 100644 index 848172f957..0000000000 --- a/SConstruct +++ /dev/null @@ -1,101 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -import SCons.Script.Main -import config -import config.ui -import platform -import os -import subprocess -import re - -# -# We try to mimic the typical autotools-workflow. -# -# * In a 'configure' step all the essential build parameters are established -# (either by explicit command-line arguments or from configure checks) -# * A subsequent build step can then simply read the cached variables, so -# users don't have to memorize and re-issue the arguments on each subsequent -# invocation, and neither do the config checks need to be re-run. -# -# The essential part here is to define a 'config' target, which removes any -# caches that may still be lingering around, then runs the checks. - -if 'config' in COMMAND_LINE_TARGETS: - # Clear the cache - try: os.remove('bin.SCons/config.py') - except: pass -if not os.path.exists('bin.SCons/'): - os.mkdir('bin.SCons/') -vars = Variables('bin.SCons/config.py', ARGUMENTS) -config.add_options(vars) -arch = ARGUMENTS.get('arch', platform.machine()) -env_vars = {} -if 'CXX' in os.environ: env_vars['CXX'] = os.environ['CXX'] -if 'CXXFLAGS' in os.environ: env_vars['CXXFLAGS'] = os.environ['CXXFLAGS'].split() -env_vars['ENV'] = os.environ #{'PATH': os.environ['PATH'], 'TMP' : os.environ['TMP']} -env = Environment(toolpath=['config/tools'], - tools=['default', 'libs', 'tests', 'doc', 'sphinx4scons'], - variables=vars, - TARGET_ARCH=arch, - **env_vars) -if 'gcc' in env['TOOLS']: - # Earlier SCons versions (~ 2.3.0) can't handle CXX=clang++. - version = subprocess.check_output([env['CXX'], '--version']) - match = re.search(r'[0-9]+(\.[0-9]+)+', version) - if match: - version = match.group(0) - else: - version = 'unknown' - env['CXXVERSION'] = version - -Help(config.ui.help(vars, env) + """ -Variables are saved in bin.SCons/config.py and persist between scons invocations. -""") - -if GetOption('help'): - Return() - -build_dir = config.prepare_build_dir(env) -config_log = '{}/config.log'.format(build_dir) - -# configure -SConsignFile('{}/.sconsign'.format(build_dir)) -#env.Decider('MD5-timestamp') -env.Decider('timestamp-newer') -checks = config.get_checks(env) -if 'config' in COMMAND_LINE_TARGETS: - conf=env.Configure(custom_tests=checks, log_file=config_log, conf_dir=build_dir) - if False in (getattr(conf, c)() for c in checks): - Exit(1) - env = conf.Finish() - vars.Save('bin.SCons/config.py', env) - -if not os.path.exists(config_log): - print('Please run `scons config` first. (See `scons -h` for available options.)') - Exit(1) - -if not GetOption('verbose'): - config.ui.pretty_output(env) - -# build -env['BPL_VERSION'] = '1.65' -for e in config.variants(env): - variant_dir=e.subst("$BOOST_CURRENT_VARIANT_DIR") - e.SConscript('src/SConscript', variant_dir=variant_dir + '/src', - exports = { 'env' : e.Clone(BOOST_LIB = 'python') }) - if 'test' in COMMAND_LINE_TARGETS: - test_env = e.Clone(BOOST_LIB = 'python', BOOST_TEST = True) - test_env.BoostUseLib('python') - e.SConscript('test/SConscript', variant_dir=variant_dir + '/test', - exports = { 'env' : test_env }) - -if 'doc' in COMMAND_LINE_TARGETS: - env.SConscript('doc/SConscript', variant_dir='bin.SCons/doc', - exports = { 'env' : e.Clone(BOOST_LIB = 'python') }) diff --git a/build.jam b/build.jam new file mode 100644 index 0000000000..e9eb1a11a2 --- /dev/null +++ b/build.jam @@ -0,0 +1,41 @@ +# Copyright René Ferdinand Rivera Morell 2024 +# 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) + +require-b2 5.2 ; + +constant boost_dependencies : + /boost/align//boost_align + /boost/bind//boost_bind + /boost/config//boost_config + /boost/conversion//boost_conversion + /boost/core//boost_core + /boost/detail//boost_detail + /boost/foreach//boost_foreach + /boost/function//boost_function + /boost/iterator//boost_iterator + /boost/lexical_cast//boost_lexical_cast + /boost/mpl//boost_mpl + /boost/numeric_conversion//boost_numeric_conversion + /boost/preprocessor//boost_preprocessor + /boost/static_assert//boost_static_assert + /boost/tuple//boost_tuple + /boost/type_traits//boost_type_traits + /boost/utility//boost_utility ; + +project /boost/python + : common-requirements + include + ; + +explicit + [ alias boost_python : build//boost_python ] + [ alias boost_numpy : build//boost_numpy ] + [ alias all : boost_python boost_numpy test ] + ; + +call-if : boost-library python + : install boost_python boost_numpy + ; + diff --git a/build/Jamfile b/build/Jamfile index 1fd6d0301a..c8f9859c64 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -7,7 +7,6 @@ import indirect ; import modules ; import feature ; import property ; - import python ; if ! [ python.configured ] && ! ( --without-python in [ modules.peek : ARGV ] ) @@ -31,157 +30,146 @@ else ; } -py2-version = [ py-version 2 ] ; -py3-version = [ py-version 3 ] ; +constant boost_dependencies_private : + /boost/graph//boost_graph + /boost/integer//boost_integer + /boost/property_map//boost_property_map + /boost/smart_ptr//boost_smart_ptr + ; -project boost/python +project : source-location ../src + : common-requirements $(boost_dependencies) + : requirements $(boost_dependencies_private) ; rule cond ( test ? : yes * : no * ) { if $(test) { return $(yes) ; } else { return $(no) ; } } rule unless ( test ? : yes * : no * ) { if ! $(test) { return $(yes) ; } else { return $(no) ; } } local rule eq ( a : b ) { if $(a) = $(b) { return 1 ; } } -lib_boost_python(2) = boost_python ; -lib_boost_python(3) = boost_python3 ; - -lib_boost_python($(py2-version)) = $(lib_boost_python(2)) ; -lib_boost_python($(py3-version)) = $(lib_boost_python(3)) ; - -rule lib_boost_python ( version ) +rule tag ( name : type ? : property-set ) { - lib $(lib_boost_python($(version))) - : # sources - list.cpp - long.cpp - dict.cpp - tuple.cpp - str.cpp - slice.cpp - - converter/from_python.cpp - converter/registry.cpp - converter/type_id.cpp - object/enum.cpp - object/class.cpp - object/function.cpp - object/inheritance.cpp - object/life_support.cpp - object/pickle_support.cpp - errors.cpp - module.cpp - converter/builtin_converters.cpp - converter/arg_to_python_base.cpp - object/iterator.cpp - object/stl_iterator.cpp - object_protocol.cpp - object_operators.cpp - wrapper.cpp - import.cpp - exec.cpp - object/function_doc_signature.cpp - : # requirements - static:BOOST_PYTHON_STATIC_LIB - BOOST_PYTHON_SOURCE - - # On Windows, all code using Python has to link to the Python - # import library. - # - # On *nix we never link libboost_python to libpython. When - # extending Python, all Python symbols are provided by the - # Python interpreter executable. When embedding Python, the - # client executable is expected to explicitly link to - # /python//python (the target representing libpython) itself. - # - # python_for_extensions is a target defined by Boost.Build to - # provide the Python include paths, and on Windows, the Python - # import library, as usage requirements. - [ cond [ python.configured ] : /python//python_for_extensions ] - - # we prevent building when there is no python available - # as it's not possible anyway, and to cause dependents to - # fail to build - [ unless [ python.configured ] : no ] - config-warning - - on:BOOST_DEBUG_PYTHON - $(version) - - -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag - @$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).python-tag - - @python.require-py - - : # default build - shared - : # usage requirements - static:BOOST_PYTHON_STATIC_LIB - on:BOOST_DEBUG_PYTHON - ; + if python-tag in [ RULENAMES $(__name__) ] + { + return [ $(__name__).python-tag $(name) : $(type) : $(property-set) ] ; + } } -lib_boost_numpy(2) = boost_numpy ; -lib_boost_numpy(3) = boost_numpy3 ; +if [ python.configured ] +{ -lib_boost_numpy($(py2-version)) = $(lib_boost_numpy(2)) ; -lib_boost_numpy($(py3-version)) = $(lib_boost_numpy(3)) ; +lib boost_python + : # sources + list.cpp + long.cpp + dict.cpp + tuple.cpp + str.cpp + slice.cpp + + converter/from_python.cpp + converter/registry.cpp + converter/type_id.cpp + object/enum.cpp + object/class.cpp + object/function.cpp + object/inheritance.cpp + object/life_support.cpp + object/pickle_support.cpp + errors.cpp + module.cpp + converter/builtin_converters.cpp + converter/arg_to_python_base.cpp + object/iterator.cpp + object/stl_iterator.cpp + object_protocol.cpp + object_operators.cpp + wrapper.cpp + import.cpp + exec.cpp + object/function_doc_signature.cpp + : # requirements + static:BOOST_PYTHON_STATIC_LIB + BOOST_PYTHON_SOURCE + + # On Windows, all code using Python has to link to the Python + # import library. + # + # On *nix we never link libboost_python to libpython. When + # extending Python, all Python symbols are provided by the + # Python interpreter executable. When embedding Python, the + # client executable is expected to explicitly link to + # /python//python (the target representing libpython) itself. + # + # python_for_extensions is a target defined by Boost.Build to + # provide the Python include paths, and on Windows, the Python + # import library, as usage requirements. + [ cond [ python.configured ] : /python//python_for_extensions ] + + # we prevent building when there is no python available + # as it's not possible anyway, and to cause dependents to + # fail to build + [ unless [ python.configured ] : no ] + config-warning + on:BOOST_DEBUG_PYTHON + -@%boostcpp.tag + -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag + @tag + @python.require-py + + : # default build + shared + : # usage requirements + static:BOOST_PYTHON_STATIC_LIB + on:BOOST_DEBUG_PYTHON + BOOST_PYTHON_NO_LIB + ; -rule lib_boost_numpy ( version ) -{ - numpy-include = [ python.numpy-include ] ; - lib $(lib_boost_numpy($(version))) - : # sources - numpy/dtype.cpp - numpy/matrix.cpp - numpy/ndarray.cpp - numpy/numpy.cpp - numpy/scalars.cpp - numpy/ufunc.cpp - : # requirements - static:BOOST_NUMPY_STATIC_LIB - BOOST_NUMPY_SOURCE - [ cond [ python.numpy ] : /python//python_for_extensions ] - [ unless [ python.numpy ] : no ] - $(numpy-include) - $(lib_boost_python($(version))) - on:BOOST_DEBUG_PYTHON - $(version) - - -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag - @$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).python-tag - - @python.require-py - - : # default build - shared - : # usage requirements - static:BOOST_NUMPY_STATIC_LIB - on:BOOST_DEBUG_PYTHON - ; } +else +{ -libraries = ; +alias boost_python : config-warning ; -for local N in 2 3 +} + +if [ python.configured ] && [ python.numpy ] { - if $(py$(N)-version) - { - lib_boost_python $(py$(N)-version) ; - libraries += $(lib_boost_python($(py$(N)-version))) ; - } - else - { - alias $(lib_boost_python($(N))) ; - } - if $(py$(N)-version) && [ python.numpy ] - { - lib_boost_numpy $(py$(N)-version) ; - libraries += $(lib_boost_numpy($(py$(N)-version))) ; - } - else - { - alias $(lib_boost_numpy($(N))) ; - } + +numpy-include = [ python.numpy-include ] ; +lib boost_numpy + : # sources + numpy/dtype.cpp + numpy/matrix.cpp + numpy/ndarray.cpp + numpy/numpy.cpp + numpy/scalars.cpp + numpy/ufunc.cpp + : # requirements + static:BOOST_NUMPY_STATIC_LIB + BOOST_NUMPY_SOURCE + [ cond [ python.numpy ] : /python//python_for_extensions ] + [ unless [ python.numpy ] : no ] + /python//numpy + boost_python + on:BOOST_DEBUG_PYTHON + -@%boostcpp.tag + -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag + @tag + @python.require-py + + : # default build + shared + : # usage requirements + static:BOOST_NUMPY_STATIC_LIB + on:BOOST_DEBUG_PYTHON + BOOST_NUMPY_NO_LIB + ; + } +else +{ + +alias boost_numpy : config-warning ; -boost-install $(libraries) ; +} diff --git a/config/__init__.py b/config/__init__.py deleted file mode 100644 index 704124b6b8..0000000000 --- a/config/__init__.py +++ /dev/null @@ -1,140 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from SCons.Variables import * -from SCons.Script import AddOption -from collections import OrderedDict -import platform -from . import ui -from . import cxx -from . import python -from . import numpy -from . import boost - -def add_options(vars): - ui.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose mode: print full commands.') - ui.add_option('--no-numpy', dest='numpy', action='store_false', help='do not attempt to build NumPy bindings.') - python.add_options(vars) - numpy.add_options(vars) - boost.add_options(vars) - - vars.Add('CXX') - vars.Add('CPPPATH', converter=lambda v:v.split()) - vars.Add('CCFLAGS', converter=lambda v:v.split()) - vars.Add('CXXFLAGS', converter=lambda v:v.split()) - vars.Add('LIBPATH', converter=lambda v:v.split()) - vars.Add('LIBS', converter=lambda v:v.split()) - vars.Add('PYTHON') - vars.Add('PYTHONLIBS') - vars.Add('prefix') - vars.Add('boostbook_prefix') - vars.Add('CXX11') - vars.Add('NUMPY') - vars.Add('NUMPY_CPPPATH', converter=lambda v:v.split()) - - ui.add_variable(vars, ("arch", "target architeture", platform.machine())) - ui.add_variable(vars, ("toolchain", "toolchain to use", 'gcc')) - ui.add_variable(vars, ListVariable("variant", "Build configuration", "release", ["release", "debug", "profile"])) - ui.add_variable(vars, ListVariable("link", "Library linking", "dynamic", ["static", "dynamic"])) - ui.add_variable(vars, ListVariable("threading", "Multi-threading support", "multi", ["single", "multi"])) - ui.add_variable(vars, EnumVariable("layout", "Layout of library names and header locations", "versioned", ["versioned", "system"])) - ui.add_variable(vars, PathVariable("stagedir", "If --stage is passed install only compiled library files in this location", "stage", PathVariable.PathAccept)) - ui.add_variable(vars, PathVariable("prefix", "Install prefix", "/usr/local", PathVariable.PathAccept)) - - -def get_checks(env): - checks = OrderedDict() - checks['cxx'] = cxx.check - checks['python'] = python.check - if env.GetOption('numpy') is not False: - checks['numpy'] = numpy.check - else: - env['NUMPY'] = False - checks['boost'] = boost.check - return checks - - -def set_property(env, **kw): - - from toolchains.gcc import features as gcc_features - from toolchains.msvc import features as msvc_features - - if 'gcc' in env['TOOLS']: features = gcc_features - elif 'msvc' in env['TOOLS']: features = msvc_features - else: raise Error('unknown toolchain') - features.init_once(env) - for (prop,value) in kw.items(): - getattr(features, prop, lambda x, y : None)(env, value) - env[prop.upper()] = value - - -def boost_suffix(env): - suffix = str() - - if env["layout"] == "versioned": - if "gcc" in env["TOOLS"]: - if env['CXX'] in ('clang', 'clang++'): - suffix += "-clang" + "".join(env["CXXVERSION"].split(".")[0:2]) - else: # assume g++ - suffix += "-gcc" + "".join(env["CXXVERSION"].split(".")[0:2]) - if env["THREADING"] == "multi": - suffix += "-mt" - if env["DEBUG"]: - suffix += "-d" - if env["layout"] == "versioned": - suffix += "-" + "_".join(env["BPL_VERSION"].split(".")) - - return suffix - - -def prepare_build_dir(env): - - vars = {} - env["boost_suffix"] = boost_suffix - build_dir="bin.SCons" - # FIXME: Support 'toolchain' variable properly. - # For now, we simply check whether $CXX refers to clang or gcc. - if "gcc" in env["TOOLS"]: - if env['CXX'] in ('clang', 'clang++'): - build_dir+="/clang-%s"%env["CXXVERSION"] - else: # assume g++ - build_dir+="/gcc-%s"%env["CXXVERSION"] - default_cxxflags = ['-ftemplate-depth-128', '-Wall', '-g', '-O2'] - vars['CXXFLAGS'] = env.get('CXXFLAGS', default_cxxflags) - elif "msvc" in env["TOOLS"]: - build_dir+="/msvc-%s"%env["MSVS_VERSION"] - vars['BOOST_BUILD_DIR'] = build_dir - vars['BOOST_SUFFIX'] = "${boost_suffix(__env__)}" - env.Replace(**vars) - return build_dir - - -def variants(env): - - env.Prepend(CPPPATH = "#/include", CPPDEFINES = ["BOOST_ALL_NO_LIB=1"]) - set_property(env, architecture = env['TARGET_ARCH']) - for variant in env["variant"]: - e = env.Clone() - e["current_variant"] = variant - set_property(env, profile = False) - if variant == "release": - set_property(e, optimize = "speed", debug = False) - elif variant == "debug": - set_property(e, optimize = "no", debug = True) - elif variant == "profile": - set_property(e, optimize = "speed", profile = True, debug = True) - for linking in env["link"]: - e["linking"] = linking - if linking == "dynamic": - e["LINK_DYNAMIC"] = True - else: - e["LINK_DYNAMIC"] = False - for threading in e["threading"]: - e["current_threading"] = threading - set_property(e, threading = threading) - yield e diff --git a/config/boost.py b/config/boost.py deleted file mode 100644 index a7cde93e66..0000000000 --- a/config/boost.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import ui -import os - -def add_options(vars): - - ui.add_option("--boost-prefix", dest="boost_prefix", type="string", nargs=1, action="store", - metavar="DIR", default=os.environ.get("BOOST_DIR"), - help="prefix for Boost libraries; should have 'include' and 'lib' subdirectories, 'boost' and 'stage\\lib' subdirectories on Windows") - ui.add_option("--boost-include", dest="boost_include", type="string", nargs=1, action="store", - metavar="DIR", help="location of Boost header files") - ui.add_option("--boostbook-prefix", dest="boostbook_prefix", type="string", - nargs=1, action="store", - metavar="DIR", default="/usr/share/boostbook", - help="prefix for BoostBook stylesheets") - -def check(context): - - boost_source_file = r"#include " - - context.Message('Checking for Boost...') - - boost_prefix = context.env.GetOption('boost_prefix') - boost_include = context.env.GetOption('boost_include') - boostbook_prefix = context.env.GetOption('boostbook_prefix') - incpath=None - if boost_include: - incpath=boost_include - elif boost_prefix: - incpath=boost_prefix - if incpath: - context.env.AppendUnique(CPPPATH=[incpath]) - if not context.TryCompile(boost_source_file, '.cpp'): - context.Result(0) - return False - context.env.AppendUnique(boostbook_prefix=boostbook_prefix) - context.Result(1) - return True diff --git a/config/cxx.py b/config/cxx.py deleted file mode 100644 index deb7be73ac..0000000000 --- a/config/cxx.py +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import ui -import os - -def add_options(vars): - - pass - -def check(context): - - source = r"""#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1800) -#error no C++11 -#endif""" - - context.Message('Checking for C++11 support...') - - if not context.TryCompile(source, '.cpp'): - context.env['CXX11'] = False - context.Result(0) - else: - context.env['CXX11'] = True - context.Result(1) - return True diff --git a/config/numpy.py b/config/numpy.py deleted file mode 100644 index 3380ae565d..0000000000 --- a/config/numpy.py +++ /dev/null @@ -1,86 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import ui -from contextlib import contextmanager - -@contextmanager -def saved(context): - save_cpppath = context.env.get('CPPPATH', []) - save_libs = context.env.get('LIBS', []) - yield context - context.env.Replace(LIBS=save_libs) - context.env.Replace(CPPPATH=save_cpppath) - - -def add_options(vars): - - pass - - -def check(context): - - numpy_source_file = r""" -// If defined, enforces linking againg PythonXXd.lib, which -// is usually not included in Python environments. -#undef _DEBUG -#include "Python.h" -#include "numpy/arrayobject.h" - -#if PY_VERSION_HEX >= 0x03000000 -void *initialize() { import_array();} -#else -void initialize() { import_array();} -#endif - -int main() -{ - int result = 0; - Py_Initialize(); - initialize(); - if (PyErr_Occurred()) - { - result = 1; - } - else - { - npy_intp dims = 2; - PyObject * a = PyArray_SimpleNew(1, &dims, NPY_INT); - if (!a) result = 1; - Py_DECREF(a); - } - Py_Finalize(); - return result; -} -""" - - import platform - import subprocess - import re, os - - def check_python(cmd): - try: - return True, subprocess.check_output([python, '-c', cmd]).strip() - except subprocess.CalledProcessError as e: - return False, e - - context.Message('Checking for NumPy...') - with saved(context): - python = context.env['PYTHON'] - result, numpy_incpath = check_python('import numpy; print(numpy.get_include())') - if result: - context.env.AppendUnique(CPPPATH=numpy_incpath) - context.env.AppendUnique(LIBS=context.env['PYTHONLIBS']) - result, output = context.TryRun(numpy_source_file,'.cpp') - if not result: - context.Result(0) - return False - context.env['NUMPY'] = True - context.env['NUMPY_CPPPATH'] = numpy_incpath - context.Result(1) - return True diff --git a/config/python.py b/config/python.py deleted file mode 100644 index 0aff24ee6d..0000000000 --- a/config/python.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import ui - -def add_options(vars): - - ui.add_option('--python', help='the python executable') - - -def check(context): - - python_source_file = r""" -// If defined, enforces linking againg PythonXXd.lib, which -// is usually not included in Python environments. -#undef _DEBUG -#include "Python.h" -int main() -{ - Py_Initialize(); - Py_Finalize(); - return 0; -} -""" - - import platform - import subprocess - import re, os - - def check_python(cmd): - return subprocess.check_output([python, '-c', cmd]).strip() - - def check_sysconfig(cmd): - r = check_python('import distutils.sysconfig as c; print(c.%s)'%cmd) - return r if r != 'None' else '' - - context.Message('Checking for Python...') - python = context.env.GetOption('python') or 'python' - context.env['PYTHON'] = python - incpath = check_sysconfig('get_python_inc()') - context.env.AppendUnique(CPPPATH=[incpath]) - if platform.system() == 'Windows': - version = check_python('import sys; print("%d%d"%sys.version_info[0:2])') - prefix = check_python('import sys; print(sys.prefix)') - libfile = os.path.join(prefix, 'libs', 'python%s.lib'%version) - libpath = os.path.join(prefix, 'libs') - lib = 'python%s'%version - context.env.AppendUnique(LIBS=[lib]) - else: - libpath = check_sysconfig('get_config_var("LIBDIR")') - libfile = check_sysconfig('get_config_var("LIBRARY")') - match = re.search('(python.*)\.(a|so|dylib)', libfile) - lib = None - if match: - lib = match.group(1) - context.env.AppendUnique(PYTHONLIBS=[lib]) - if match.group(2) == 'a': - flags = check_sysconfig('get_config_var("LINKFORSHARED")') - if flags is not None: - context.env.AppendUnique(LINKFLAGS=flags.split()) - context.env.AppendUnique(LIBPATH=[libpath]) - oldlibs = context.AppendLIBS([lib]) - flags = check_sysconfig('get_config_var("MODLIBS")') - flags += ' ' + check_sysconfig('get_config_var("SHLIBS")') - flags = [f[2:] for f in flags.strip().split() if f.startswith('-l')] - if flags: - context.AppendLIBS([flags]) - result = context.TryLink(python_source_file,'.cpp') - if not result and context.env['PLATFORM'] == 'darwin': - # Sometimes we need some extra stuff on Mac OS - frameworkDir = libpath # search up the libDir tree for the proper home for frameworks - while frameworkDir and frameworkDir != "/": - frameworkDir, d2 = os.path.split(frameworkDir) - if d2 == "Python.framework": - if not "Python" in os.listdir(os.path.join(frameworkDir, d2)): - context.Result(0) - print(( - "Expected to find Python in framework directory %s, but it isn't there" - % frameworkDir)) - return False - break - context.env.AppendUnique(LINKFLAGS="-F%s" % frameworkDir) - result = context.TryLink(python_source_file,'.cpp') - if not result: - context.Result(0) - print("Cannot link program with Python.") - return False - if context.env['PLATFORM'] == 'darwin': - context.env['LDMODULESUFFIX'] = '.so' - context.Result(1) - context.SetLIBS(oldlibs) - context.env.AppendUnique(PYTHONLIBS=[lib] + flags) - return True diff --git a/config/toolchains/__init__.py b/config/toolchains/__init__.py deleted file mode 100644 index af38285a65..0000000000 --- a/config/toolchains/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -import traceback - -def append_feature_flag(env, **kw): - stack = traceback.extract_stack(limit = 3) - feature = stack[0][2].upper() - for (key, val) in kw.items(): - feature_var = feature + "_" + key - env.AppendUnique(**{ key : "$" + feature_var }) - env[feature_var] = val - diff --git a/config/toolchains/gcc.py b/config/toolchains/gcc.py deleted file mode 100644 index 200ecb6a7e..0000000000 --- a/config/toolchains/gcc.py +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import append_feature_flag - -class features: - - @classmethod - def init_once(cls, env): - pass - - @staticmethod - def architecture(env, arch): - if arch: - flag = {'x86' : '-m32', - 'x86_64' : '-m64',}.get(arch) - if flag: - append_feature_flag(env, CCFLAGS = flag) - - @staticmethod - def optimize(env, optimize): - if not optimize or optimize == "no": - append_feature_flag(env, CCFLAGS = "-O0 -fno-inline") - elif optimize == "speed": - append_feature_flag(env, CCFLAGS = "-O3 -finline-functions -Wno-inline") - elif optimize == "space": - append_feature_flag(env, CCFLAGS = "-Os") - else: - append_feature_flag(env, CCFLAGS = "") - - @staticmethod - def profile(env, profile): - if profile: - append_feature_flag(env, CCFLAGS = "-pg", LINKFLAGS = "-pg") - else: - append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "") - - @staticmethod - def threading(env, threading): - if threading == "multi": - append_feature_flag(env, CCFLAGS = "-pthread", LINKFLAGS = "-pthread") - else: - append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "") - - @staticmethod - def debug(env, debug): - if debug: - append_feature_flag(env, CCFLAGS = "-g", CPPDEFINES = []) - else: - append_feature_flag(env, CCFLAGS = "", CPPDEFINES = "NDEBUG") diff --git a/config/toolchains/msvc.py b/config/toolchains/msvc.py deleted file mode 100644 index 576ff819c6..0000000000 --- a/config/toolchains/msvc.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from . import append_feature_flag - -class features: - - @classmethod - def init_once(cls, env): - env.AppendUnique(CCFLAGS = ['-TP', '/Z7', '/W3' ,'/GR', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/wd4675', '/EHs']) - env.AppendUnique(LINKFLAGS = ['/subsystem:console']) - - @staticmethod - def architecture(env, arch): - if arch: - flag = {'x86' : '/MACHINE:X86', - 'x86_64' : '/MACHINE:X64',}.get(arch) - if flag: - append_feature_flag(env, LINKFLAGS = flag) - - @staticmethod - def optimize(env, optimize): - #if not optimize or optimize == "no": - # append_feature_flag(env, CCFLAGS = "-O0 -fno-inline") - #elif optimize == "speed": - # append_feature_flag(env, CCFLAGS = "-O3 -finline-functions -Wno-inline") - #elif optimize == "space": - # append_feature_flag(env, CCFLAGS = "-Os") - #else: - append_feature_flag(env, CCFLAGS = "") - - @staticmethod - def profile(env, profile): - #if profile: - # append_feature_flag(env, CCFLAGS = "-pg", LINKFLAGS = "-pg") - #else: - append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "") - - @staticmethod - def threading(env, threading): - #if threading == "multi": - # append_feature_flag(env, CCFLAGS = "/MT") - #else: - # append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "") - pass - - @staticmethod - def debug(env, debug): - #if debug: - # append_feature_flag(env, CCFLAGS = "-g", CPPDEFINES = []) - #else: - append_feature_flag(env, CCFLAGS = "", CPPDEFINES = "NDEBUG") diff --git a/config/tools/clang.py b/config/tools/clang.py deleted file mode 100644 index 9af06708e6..0000000000 --- a/config/tools/clang.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -# Based on SCons/Tool/gcc.py - -import os -import re -import subprocess - -import SCons.Util -import SCons.Tool.cc - -compilers = ['clang'] - -def generate(env): - """Add Builders and construction variables for clang to an Environment.""" - SCons.Tool.cc.generate(env) - - env['CC'] = env.Detect(compilers) or 'clang' - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - else: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') - # determine compiler version - if env['CC']: - #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], - pipe = SCons.Action._subproc(env, [env['CC'], '--version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - # clang -dumpversion is of no use - line = pipe.stdout.readline() - match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line) - if match: - env['CCVERSION'] = match.group(1) - -def exists(env): - return env.Detect(compilers) diff --git a/config/tools/doc.py b/config/tools/doc.py deleted file mode 100644 index 5e81f37906..0000000000 --- a/config/tools/doc.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from SCons.Script import AddOption, Flatten -from SCons.Script import Builder -from SCons.Action import Action -from SCons.Defaults import Copy -from SCons.Script import * -from subprocess import check_output, STDOUT, CalledProcessError -import sys -import os - -def QuickBook(env, target, source, dependencies=[]): - """Compile a QuickBook document to BoostBook.""" - - for d in dependencies: - env.Depends(target, d) - env.Command(target, source, 'quickbook --input-file=$SOURCE --output-file=$TARGET') - - -def BoostBook(env, target, source, resources=[], args=[]): - """Compile a BoostBook document to DocBook.""" - - bb_prefix = env.GetOption('boostbook_prefix') - stylesheet = bb_prefix + '/xsl/docbook.xsl' - env.Command(target, source, - 'xsltproc {} -o $TARGET {} $SOURCE'.format(' '.join(args), stylesheet)) - - -def BoostHTML(env, target, source, resources=[], args=[]): - """Compile a DocBook document to HTML.""" - - bb_prefix = env.GetOption('boostbook_prefix') - stylesheet = bb_prefix + '/xsl/html.xsl' - env.Command(target, source, - 'xsltproc {} -o $TARGET/ {} $SOURCE'.format(' '.join(args), stylesheet)) - prefix=Dir('.').path - for r in resources: - r = File(r).path[len(prefix)+1:] - env.Depends(target, target + r) - env.Command(target + r, r, Copy('$TARGET', '$SOURCE')) - - -def BoostRST(env, target, source, resources=[]): - """Compile an RST document to HTML.""" - - prefix=Dir('.').path - for r in resources: - r = File(r).path[len(prefix)+1:] - env.Depends('html/' + r, r) - env.Command('html/' + r, r, Copy('$TARGET', '$SOURCE')) - env.Command(target, source, - 'rst2html --link-stylesheet --traceback --trim-footnote-reference-space --footnote-references=superscript --stylesheet=rst.css $SOURCE $TARGET') - - -def BoostSphinx(env, target, source): - env.Sphinx(target, source) - - -def exists(env): - return True - - -def generate(env): - - env.AddMethod(QuickBook) - env.AddMethod(BoostBook) - env.AddMethod(BoostHTML) - env.AddMethod(BoostRST) - env.AddMethod(BoostSphinx) diff --git a/config/tools/libs.py b/config/tools/libs.py deleted file mode 100644 index da8119d042..0000000000 --- a/config/tools/libs.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from SCons.Script import AddOption, COMMAND_LINE_TARGETS, BUILD_TARGETS -import distutils.sysconfig -import platform - - -def BoostLibrary(env, lib, sources, make_aliases = True, **kw): - if env["LINK_DYNAMIC"]: - lib_node = env.SharedLibrary("boost_" + lib + env["BOOST_SUFFIX"], sources, **kw) - else: - lib_node = env.StaticLibrary("boost_" + lib + env["BOOST_SUFFIX"], sources, **kw) - - if make_aliases: - if env.GetOption("stage"): - env.Alias(lib, env.Install(env.Dir("$stagedir", "#"), lib_node)) - env.Default(env.Alias(lib, lib_node)) - - if env.GetOption("install"): - env.Alias(lib, env.Install("$prefix/lib", lib_node)) - env.Alias(lib, env.Install('$prefix/include/boost', '#/include/boost/python')) - env.Alias(lib, env.Install('$prefix/include/boost', '#/include/boost/python.hpp')) - return lib_node - - -def BoostUseLib(env, lib): - build_dir = env.Dir('$BOOST_CURRENT_VARIANT_DIR/src') - env.AppendUnique(LIBPATH = [build_dir], - LIBS = ["boost_" + lib + env["BOOST_SUFFIX"]]) - if env.get("BOOST_TEST"): - env.AppendUnique(RPATH = [build_dir]) - if platform.system() == 'Windows': - env.PrependENVPath('PATH', build_dir.abspath) - else: - env.PrependENVPath('LD_LIBRARY_PATH', build_dir.abspath) - - -def PythonExtension(env, lib, sources, **kw): - if env["LINK_DYNAMIC"]: - ext = env.SharedLibrary(lib, sources, SHLIBPREFIX='', SHLIBSUFFIX=distutils.sysconfig.get_config_var("SO"), **kw) - return ext - - -def boost_copy_func(dest, source, env): - import os, stat, shutil - - if os.path.isdir(source): - if os.path.exists(dest): - if not os.path.isdir(dest): - raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source)) - else: - os.makedirs(dest) - for file in os.listdir(source): - if file == ".svn": continue - boost_copy_func(os.path.join(dest, file), os.path.join(source, file), env) - else: - shutil.copy2(source, dest) - st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - return 0 - - -def exists(env): - return True - - -def generate(env): - env.AddMethod(BoostLibrary) - env.AddMethod(BoostUseLib) - env.AddMethod(PythonExtension) - - env.Replace( - INSTALL = boost_copy_func, - BOOST_CURRENT_VARIANT_DIR = "#/$BOOST_BUILD_DIR/$current_variant/$linking/threading-$current_threading" - ) - - AddOption('--stage', dest='stage', action="store_true") - AddOption('--install', dest='install', action="store_true") diff --git a/config/tools/sphinx4scons.py b/config/tools/sphinx4scons.py deleted file mode 100644 index dda88d23f3..0000000000 --- a/config/tools/sphinx4scons.py +++ /dev/null @@ -1,592 +0,0 @@ -"""SCons.Tool.spinx4scons - -Tool-specific initialization for the Sphinx document build system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -It should be placed in e.g. ~/site_scons/site_tools/sphinx4scons/ -directory. Then it may be loaded by placing - - sphinx = Tool('sphinx4scons') - sphinx(env) - -in your SConstruct file. - -For further details, please see the SCons documentation on how to -install and enable custom tools. -""" - -# -# This package is provided under the Expat license -# -# Copyright (c) 2012 Orlando Wingbrant -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__author__ = "Orlando Wingbrant" -__email__ = "orlando@widesite.org" -__url__ = "https://bitbucket.org/wingbrant/sphinx4scons" -__license__ = "Expat license" - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Util -import SCons.Node.FS -import os - -from sphinx.util.matching import patfilter, compile_matchers -from sphinx.util.osutil import make_filename - - -class ToolSphinxWarning(SCons.Warnings.Warning): - pass - - -class SphinxBuilderNotFound(ToolSphinxWarning): - pass - -SCons.Warnings.enableWarningClass(ToolSphinxWarning) - - -def exists(env): - return _detect(env) - - -def _detect(env): - """Try to detect the sphinx-build script.""" - try: - return env['SPHINXBUILD'] - except KeyError: - pass - - sphinx = env.WhereIs('sphinx-build') - if sphinx: - return sphinx - - raise SCons.Errors.StopError( - SphinxBuilderNotFound, - "Could not detect sphinx-build script") - return None - - -def generate(env): - """Add Builders and construction variables to the Environment.""" - - env['SPHINXBUILD'] = _detect(env) - sphinx = _create_sphinx_builder(env) - - env.SetDefault( - # Additional command-line flags - SPHINXFLAGS = '', - - # Tag definitions, each entry will appear on the command line preceded by -t - SPHINXTAGS = [], - - # Directory for doctrees - SPHINXDOCTREE = '', - - # Path to sphinx configuration file - SPHINXCONFIG = '', - - # Config file override settings, each entry will be preceded by -D - SPHINXSETTINGS = {}, - - # Default sphinx builder, - SPHINXBUILDER = 'html', - - # Sphinx command - SPHINXCOM = "$SPHINXBUILD $_SPHINXOPTIONS ${SOURCE.attributes.root} ${TARGET.attributes.root}", - - # Alternate console output when building sphinx documents - SPHINXCOMSTR = "" - ) - - try: - env.AddMethod(Sphinx, "Sphinx") - except AttributeError: - # Looks like we use a pre-0.98 version of SCons... - from SCons.Script.SConscript import SConsEnvironment - SConsEnvironment.Sphinx = Sphinx - - -def Sphinx(env, target, source, **kw): - """A pseudo-builder wrapper for the sphinx builder.""" - builder = env['BUILDERS']['Sphinx4Scons'] - env_kw = env.Override(kw) - options = _get_sphinxoptions(env_kw, target, source) - output = builder(env, target, source, _SPHINXOPTIONS=options, **kw) - return output - - -def _get_sphinxoptions(env, target, source): - """Concatenates all the options for the sphinx command line.""" - options = [] - - builder = _get_sphinxbuilder(env) - options.append("-b %s" % env.subst(builder, target=target, source=source)) - - flags = env.get('options', env.get('SPHINXFLAGS', '')) - options.append(env.subst(flags, target=target, source=source)) - - tags = env.get('tags', env.get('SPHINXTAGS', None)) - if tags is not None: - if not SCons.SCons.Util.is_List(tags): - tags = [tags] - for tag in tags: - if tag != '': - tag = env.subst(tag, target=target, source=source) - options.append("-t %s" % tag) - - settings = env.get('settings', env.get('SPHINXSETTINGS', None)) - if settings is not None: - if not SCons.SCons.Util.is_Dict(settings): - raise TypeError('SPHINXSETTINGS and/or settings argument must be a dictionary') - for key, value in settings.iteritems(): - if value != '': - value = env.subst(value, target=target, source=source) - options.append('-D "%s=%s"' % (key, value)) - - doctree = env.get('doctree', env.get("SPHINXDOCTREE", None)) - if isinstance(doctree, SCons.Node.FS.Dir): - options.append("-d %s" % doctree.get_abspath()) - elif doctree is not None and doctree != '': - doctree = env.subst(doctree, target=target, source=source) - options.append("-d %s" % env.Dir(doctree).get_abspath()) - - config = _get_sphinxconfig_path(env, None) - if config is not None and config != '': - config = env.subst(config, target=target, source=source) - options.append("-c %s" % env.Dir(config).File('conf.py').rfile().dir.get_abspath()) - return " ".join(options) - - -def _create_sphinx_builder(env): - try: - sphinx = env['BUILDERS']['Sphinx4Scons'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - sphinx_com = SCons.Action.Action('$SPHINXCOM', '$SPHINXCOMSTR') - sphinx = SCons.Builder.Builder(action=sphinx_com, - emitter=sphinx_emitter, - target_factory=fs.Dir, - source_factory=fs.Dir - ) - env['BUILDERS']['Sphinx4Scons'] = sphinx - return sphinx - - -def sphinx_emitter(target, source, env): - target[0].must_be_same(SCons.Node.FS.Dir) - targetnode = target[0] - - source[0].must_be_same(SCons.Node.FS.Dir) - srcnode = source[0] - - configdir = _get_sphinxconfig_path(env, None) - if not configdir: - confignode = srcnode - else: - confignode = env.Dir(configdir) - - srcinfo = SourceInfo(srcnode, confignode, env) - targets, sources = _get_emissions(env, target, srcinfo) - env.Clean(targets, target[0]) - - return targets, sources - - -def sphinx_path(os_path): - """Create sphinx-style path from os-style path.""" - return os_path.replace(os.sep, "/") - - -def os_path(sphinx_path): - """Create os-style path from sphinx-style path.""" - return sphinx_path.replace("/", os.sep) - - -class SourceInfo(object): - """ - Data container for all different kinds of source files used in - a sphinx project. - """ - def __init__(self, srcnode, confignode, env): - self.confignode = confignode - self.config = self._get_config(self.confignode, env) - self.templates = self._get_templates(self.confignode, self.config) - self.statics = self._get_statics(self.confignode, self.config) - self.srcnode = srcnode - self.sources = self._get_sources(self.srcnode, self.config) - - self.srcroot = srcnode - if not srcnode.duplicate: - self.srcroot = srcnode.srcnode().rdir() - - - def _get_config(self, confignode, env): - config = {} - execfile(confignode.File('conf.py').rfile().get_abspath(), config) - return config - - - def _get_templates(self, confignode, config): - """Returns template files defined in the project.""" - templates = [] - for path in config.get('templates_path', []): - # Check if path is dir or file. - # We can't use FS.Entry since that will create nodes, and - # these nodes don't know about the source tree and will - # get disambiguated to files even if they are directories in the - # source tree. - p = confignode.File('conf.py').rfile().dir.srcnode().get_abspath() - p = os.path.join(p, os_path(path)) - if os.path.isfile(p): - templates.append(confignode.File(path)) - elif os.path.isdir(p): - node = confignode.Dir(path) - for root, dirs, files in os.walk(p): - mydir = node.Dir(os.path.relpath(root, p)) - templates += [mydir.File(f) for f in files] - return templates - - - def _get_statics(self, confignode, config): - """Returns static files, filtered through exclude_patterns.""" - statics = [] - matchers = compile_matchers(config.get('exclude_patterns', [])) - - for path in config.get('html_static_path', []): - # Check _get_templates() why we use this construction. - p = confignode.File('conf.py').rfile().dir.srcnode().get_abspath() - p = os.path.join(p, os_path(path)) - if os.path.isfile(p): - statics.append(confignode.File(path)) - elif os.path.isdir(p): - node = confignode.Dir(path) - for root, dirs, files in os.walk(p): - relpath = os.path.relpath(root, p) - for entry in [d for d in dirs if - self._anymatch(matchers, - sphinx_path(os.path.join(relpath, d)))]: - dirs.remove(entry) - statics += [node.File(os_path(f)) for f in - self._exclude(matchers, - [sphinx_path(os.path.join(relpath, name)) - for name in files])] - return statics - - - def _get_sources(self, srcnode, config): - """Returns all source files in the project filtered through exclude_patterns.""" - suffix = config.get('source_suffix', '.rst') - matchers = compile_matchers(config.get('exclude_patterns', [])) - - srcfiles = [] - scannode = srcnode.srcnode().rdir() - - for root, dirs, files in os.walk(scannode.get_abspath()): - relpath = os.path.relpath(root, scannode.get_abspath()) - for entry in [d for d in dirs if - self._anymatch(matchers, - sphinx_path(os.path.join(relpath, d)))]: - dirs.remove(entry) - srcfiles += [srcnode.File(os_path(f)) for f in - self._exclude(matchers, - [sphinx_path(os.path.join(relpath, name)) - for name in files if name.endswith(suffix)])] - return srcfiles - - - def _exclude(self, matchers, items): - result = items - for matcher in matchers: - result = filter(lambda x: not matcher(x), result) - return result - - - def _anymatch(self, matchers, item): - for matcher in matchers: - if matcher(item): - return True - return False - - -def _get_sphinxconfig_path(env, default): - path = env.get('config', env.get('SPHINXCONFIG', None)) - if path is None or path == '': - path = default - return path - - -def _get_emissions(env, target, srcinfo): - targets = [] - sources = [] - builder = _get_sphinxbuilder(env) - if builder == 'changes': - targets, sources = _get_changes_emissions(env, target, srcinfo) - if builder == 'devhelp': - targets, sources = _get_help_emissions(env, target, srcinfo, - ['.devhelp.gz']) - elif builder == 'dirhtml': - targets, sources = _get_dirhtml_emissions(env, target, srcinfo) - elif builder == 'doctest': - targets, sources = _get_doctest_emissions(env, target, srcinfo) - elif builder == 'epub': - targets, sources = _get_epub_emissions(env, target, srcinfo) - elif builder == 'html': - targets, sources = _get_serialize_emissions(env, target, srcinfo) - elif builder == 'htmlhelp': - targets, sources = _get_help_emissions(env, target, srcinfo, - ['.hhp'], 'htmlhelp_basename') - elif builder == 'gettext': - targets, sources = _get_gettext_emissions(env, target, srcinfo) - elif builder == 'json': - targets, sources = _get_serialize_emissions(env, target, srcinfo, - '.fjson', - ['globalcontext.json', - 'searchindex.json', - 'self.environment.pickle']) - elif builder == 'latex': - targets, sources = _get_latex_emissions(env, target, srcinfo) - elif builder == 'linkcheck': - targets, sources = _get_linkcheck_emissions(env, target, srcinfo) - elif builder == 'man': - targets, sources = _get_man_emissions(env, target, srcinfo) - elif builder == 'pickle': - targets, sources = _get_serialize_emissions(env, target, srcinfo, - '.fpickle', - ['globalcontext.pickle', - 'searchindex.pickle', - 'environment.pickle']) - elif builder == 'qthelp': - targets, sources = _get_help_emissions(env, target, srcinfo, - ['.qhp', '.qhcp']) - elif builder == 'singlehtml': - targets, sources = _get_singlehtml_emissions(env, target, srcinfo) - elif builder == 'texinfo': - targets, sources = _get_texinfo_emissions(env, target, srcinfo) - elif builder == 'text': - targets, sources = _get_text_emissions(env, target, srcinfo) - - sources.append(srcinfo.confignode.File('conf.py')) - - for s in sources: - s.attributes.root = srcinfo.srcroot - - for t in targets: - t.attributes.root = target[0] - - return targets, sources - - -def _get_sphinxbuilder(env): - builder = env.get('builder', env["SPHINXBUILDER"]) - if builder is None or builder == '': - raise SCons.Errors.UserError(("Missing construction variable " + - "SPHINXBUILDER or variable is empty.")) - return builder - - -def _get_changes_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - targets = [target[0].File("changes.html")] - return targets, sources - - -def _get_dirhtml_emissions(env, target, srcinfo): - suffix = srcinfo.config.get('html_file_suffix', ".html") - - def get_outfilename(pagename): - pagename = os.path.splitext(pagename)[0] - - #Special treatment of files named "index". Don't create directory. - if pagename == 'index' or pagename.endswith(os.sep + 'index'): - outfilename = pagename + suffix - else: - outfilename = os.path.join(pagename, 'index' + suffix) - return outfilename - - sources = [] - sources.extend(srcinfo.sources) - sources.extend(srcinfo.templates) - sources.extend(srcinfo.statics) - - targets = [] - for s in srcinfo.sources: - t = os.path.relpath(str(s), str(srcinfo.srcroot)) - targets.append(target[0].File(get_outfilename(t))) - - for key in srcinfo.config.get('html_additional_pages', {}): - t = target[0].File(get_outfilename(key)) - targets.append(t) - - return targets, sources - - -def _get_doctest_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - targets = [target[0].File("output.txt")] - return targets, sources - - -def _get_epub_emissions(env, target, srcinfo): - epubPreFiles = srcinfo.config.get('epub_pre_files', []) - epubPostFiles = srcinfo.config.get('epub_post_files', []) - epubCover = srcinfo.config.get('epub_cover', (None, None)) - - sources = [] - sources.extend(srcinfo.sources) - sources.extend([srcinfo.srcroot.File(os_path(f[0])) for f in epubPreFiles]) - sources.extend([srcinfo.srcroot.File(os_path(f[0])) for f in epubPostFiles]) - if not (epubCover[0] is None or epubCover[0] == ''): - sources.append(srcinfo.srcroot.File(os_path(epubCover[0]))) - if not (epubCover[1] is None or epubCover[1] == ''): - sources.append(srcinfo.srcroot.File(os_path(epubCover[1]))) - - t = srcinfo.config.get('epub_basename', - srcinfo.config.get('project', - 'Python')) - - targets = [target[0].File("%s.epub" % make_filename(t))] - - return targets, sources - - -def _get_gettext_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - - targets = [os.path.relpath(str(s), str(srcinfo.srcroot)) for s in sources] - targets = [os.path.splitext(t)[0] for t in targets] - targets = set([t.split(os.sep)[0] for t in targets]) - targets = [target[0].File(t + ".pot") for t in targets] - - return targets, sources - - -def _get_help_emissions(env, target, srcinfo, suffixes, basenameConfigKey='project'): - basename = make_filename( - srcinfo.config.get(basenameConfigKey, srcinfo.config['project'])) - - sources = [] - sources.extend(srcinfo.sources) - sources.extend(srcinfo.templates) - sources.extend(srcinfo.statics) - - targets = [target[0].File(basename + s) for s in suffixes] - - return targets, sources - - -def _get_latex_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - - targets = map(lambda x: target[0].File(os_path(x[1])), - srcinfo.config.get('latex_documents')) - - return targets, sources - - -def _get_linkcheck_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - targets = [target[0].File("output.txt")] - return targets, sources - - -def _get_man_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - targets = map(lambda x: target[0].File(os_path("%s.%s" % (x[1], x[4]))), - srcinfo.config.get('man_pages')) - return targets, sources - - -def _get_serialize_emissions(env, target, srcinfo, suffix=None, extrafiles=[]): - if suffix is None: - suffix = srcinfo.config.get('html_file_suffix', '.html') - - sources = [] - sources.extend(srcinfo.sources) - sources.extend(srcinfo.templates) - sources.extend(srcinfo.statics) - - targets = [] - for s in srcinfo.sources: - t = os.path.splitext(str(s))[0] + suffix - t = os.path.relpath(t, str(srcinfo.srcroot)) - targets.append(t) - - for key in srcinfo.config.get('html_additional_pages', {}): - targets.append(os_path("%s%s" % (key, suffix))) - - targets.extend(extrafiles) - targets = [target[0].File(t) for t in targets] - - return targets, sources - - -def _get_singlehtml_emissions(env, target, srcinfo): - suffix = srcinfo.config.get('html_file_suffix', ".html") - - sources = [] - sources.extend(srcinfo.sources) - sources.extend(srcinfo.templates) - sources.extend(srcinfo.statics) - - t = os.path.relpath(srcinfo.config['master_doc'] + suffix, - str(srcinfo.srcroot)) - targets = [target[0].File(t)] - - return targets, sources - - -def _get_texinfo_emissions(env, target, srcinfo): - suffix = srcinfo.config.get('source_suffix', '.rst') - - sources = [] - sources.extend(srcinfo.sources) - sources.extend(map(lambda x: source[0].File(os_path(x + suffix)), - srcinfo.config.get('texinfo_appendices', []))) - - targets = map(lambda x: target[0].File(os_path("%s.texi" % x[1])), - srcinfo.config.get('texinfo_documents')) - - return targets, sources - - -def _get_text_emissions(env, target, srcinfo): - sources = [] - sources.extend(srcinfo.sources) - - targets = [] - for s in sources: - t = os.path.relpath(str(s), str(srcinfo.srcroot)) - t = os.path.splitext(t)[0] + ".txt" - targets.append(target[0].File(t)) - - return targets, sources diff --git a/config/tools/tests.py b/config/tools/tests.py deleted file mode 100644 index a2e8e08465..0000000000 --- a/config/tools/tests.py +++ /dev/null @@ -1,123 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from SCons.Script import AddOption, Flatten -from SCons.Script import Builder -from SCons.Action import Action -from subprocess import check_output, STDOUT, CalledProcessError -import sys -import os - - -def BoostCompileTest(env, test, source = None, **kw): - - def gen_result(target, source, env=env): - target_file = target[0].abspath - result_file = os.path.splitext(target_file)[0] + '.result' - if sys.stdout.isatty(): - env['RESULT']='\033[92mPASS\033[0m' - else: - env['RESULT']='PASS' - - with open(result_file, 'w+') as result: - result.write('Result: {}\n'.format('pass')) - - obj = env.Object(test, source if source is not None else test + '.cpp') - env.AddPostAction(obj, Action(gen_result, cmdstr=None)) - env.AddPostAction(obj, Action('@echo $RESULT')) - return obj - -def BoostRun(env, prog, target, command = '$SOURCE'): - - def call(target, source, env=env): - cmd = env.subst(command, target=target, source=source) - result_file = env.subst('$TARGET', target=target) - output='' - try: - output=check_output(cmd, stderr=STDOUT, shell=True, env=env['ENV']) - success=True - except CalledProcessError as e: - output=e.output - success=False - with open(result_file, 'w+') as result: - result.write('Result: {}\n'.format(success and 'pass' or 'fail')) - result.write('Output: {}\n'.format(output)) - if sys.stdout.isatty(): - env['RESULT']=success and '\033[92mPASS\033[0m' or '\033[91mFAIL\033[0m' - else: - env['RESULT']=success and 'PASS' or 'FAIL' - - testcomstr = env.get('TESTCOMSTR') - if testcomstr: - run = env.Command(target, prog, Action(call, cmdstr=testcomstr)) - else: - run = env.Command(target, prog, Action(call, cmdstr=command)) - env.AddPostAction(target, Action('@echo $RESULT')) - return run - - -def BoostRunPythonScript(env, script): - return env.BoostRun(env.File(script), script.replace('.py', '.result'), '"${PYTHON}" $SOURCE') - - -def BoostRunTest(env, test, source = None, command = '$SOURCE', command_sources = [], **kw): - test_prog = env.Program(test, (source is None) and (test + ".cpp") or source, **kw) - command += '> $TARGET' - run = env.BoostRun([test_prog, command_sources], test + '.result', command) - return run - - -def BoostRunTests(env, tests, **kw): - run = [] - for test in Flatten(tests): - run += env.BoostRunTest(test, **kw) - return run - -def BoostCompileTests(env, tests, **kw): - comp = [] - for test in Flatten(tests): - comp += env.BoostCompileTest(test, **kw) - return comp - - -def BoostTestSummary(env, tests, **kw): - - def print_summary(target, source, **kw): - results = tests - failures = [r for r in results - if r.get_path().endswith('.result') and not 'Result: pass' in r.get_contents()] - print('%s tests; %s pass; %s fails'%(len(results), len(results)-len(failures), len(failures))) - if failures: - print('For detailed failure reports, see:') - for f in failures: - print(f.get_path()) - - testsumcomstr = env.get('TESTSUMCOMSTR') - if testsumcomstr: - run = env.Command('summary', tests, Action(print_summary, cmdstr=testsumcomstr)) - else: - run = env.Command('summary', tests, print_summary, cmdstr='') - - - - - -def exists(env): - return True - - -def generate(env): - AddOption('--test', dest='test', action="store_true") - - env.AddMethod(BoostCompileTest) - env.AddMethod(BoostRun) - env.AddMethod(BoostRunPythonScript) - env.AddMethod(BoostRunTest) - env.AddMethod(BoostRunTests) - env.AddMethod(BoostCompileTests) - env.AddMethod(BoostTestSummary) diff --git a/config/ui.py b/config/ui.py deleted file mode 100644 index e6e4f24f72..0000000000 --- a/config/ui.py +++ /dev/null @@ -1,96 +0,0 @@ -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -from SCons.Script import AddOption -import sys - -variables=[] # remember 'public' variables -options=[] - -def add_option(*args, **kwds): - """Capture the help messages so we can produce a helpful usage text.""" - options.append('{:25} {}'.format(', '.join(args), kwds.get('help', ''))) - AddOption(*args, **kwds) - -def add_variable(vars, var): - variables.append(var[0]) - vars.Add(var) - - -def options_help(env): - - return '\n '.join(options) - - -def variables_help(vars, env): - """This is cloned from SCons' Variables.GenerateHelpText, to only report 'public' variables.""" - - opts = [o for o in vars.options if o.key in variables] - - def format(opt): - if opt.key in env: - actual = env.subst('${%s}' % opt.key) - else: - actual = None - return vars.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) - text = ''.join([f for f in map(format, opts) if f]) - lines = [' %s'%l for l in text.split('\n')] # Add some indentation - return '\n'.join(lines) - - - -def help(vars, env): - - return """Usage: scons [--option...] [variable=value...] [target...] - -available options: - - {} - -available variables: - {} -""".format(options_help(env), variables_help(vars, env)) - -def pretty_output(env): - - colors = {} - colors['red'] = '\033[31m' - colors['green'] = '\033[32m' - colors['blue'] = '\033[34m' - colors['yellow'] = '\033[93m' - colors['Red'] = '\033[91m' - colors['Green'] = '\033[92m' - colors['Blue'] = '\033[94m' - colors['Purple'] = '\033[95m' - colors['Cyan'] = '\033[96m' - colors['end'] = '\033[0m' - - #If the output is not a terminal, remove the colors - if not sys.stdout.isatty(): - for key, value in colors.iteritems(): - colors[key] = '' - - compile_source_message = '{green}Compiling $TARGET{end}'.format(**colors) - compile_shared_source_message = '{green}Compiling $TARGET{end}'.format(**colors) - link_program_message = '{blue}Linking $TARGET{end}'.format(**colors) - link_library_message = '{blue}Linking $TARGET{end}'.format(**colors) - ranlib_library_message = '{blue}Ranlib $TARGET{end}'.format(**colors) - link_shared_library_message = '{blue}Linking $TARGET{end}'.format(**colors) - test_message = '{blue}Testing $SOURCE{end}'.format(**colors) - testsum_message = '{Blue}Test Summary{end}'.format(**colors) - - env.Replace(CXXCOMSTR = compile_source_message, - CCCOMSTR = compile_source_message, - SHCCCOMSTR = compile_shared_source_message, - SHCXXCOMSTR = compile_shared_source_message, - ARCOMSTR = link_library_message, - RANLIBCOMSTR = ranlib_library_message, - SHLINKCOMSTR = link_shared_library_message, - LINKCOMSTR = link_program_message, - TESTCOMSTR = test_message, - TESTSUMCOMSTR = testsum_message) diff --git a/doc/SConscript b/doc/SConscript deleted file mode 100644 index 8eb0fb9340..0000000000 --- a/doc/SConscript +++ /dev/null @@ -1,52 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -Import('env') - -env.QuickBook('python.bbk', 'python.qbk', - ['building.qbk', - 'configuration.qbk', - 'support.qbk', - 'faq.qbk', - 'glossary.qbk']) - -env.QuickBook('tutorial.bbk', 'tutorial.qbk') -env.QuickBook('reference.bbk', 'reference.qbk', - Glob('reference/*.qbk')) - - -env.BoostBook('python.dbk', 'python.bbk') -env.BoostBook('tutorial.dbk', 'tutorial.bbk') -env.BoostBook('reference.dbk', 'reference.bbk') - -images = Glob('images/*.*') + Glob('images/callouts/*.*') - -env.BoostHTML('html/', 'python.dbk', - resources=['boostbook.css'] + images, - args=['--stringparam', 'generate.toc', '"library nop; chapter toc; section toc;"', - '--stringparam', 'html.stylesheet', 'boostbook.css', - '--stringparam', 'boost.image.src', 'images/bpl.png', - '--stringparam', 'boost.graphics.root', 'images/', - '--stringparam', 'boost.defaults', 'none', - '--param', 'toc.max.depth', '3', - '--param', 'toc.section.depth' ,'2', - '--param', 'chunk.section.depth', '1']) -env.BoostHTML('html/tutorial/', 'tutorial.dbk', - args=['--stringparam', 'html.stylesheet', '../boostbook.css', - '--stringparam', 'boost.image.src', '../images/bpl.png', - '--stringparam', 'boost.graphics.root', '../images/']) -env.BoostHTML('html/reference/', 'reference.dbk', - args=['--stringparam', 'html.stylesheet', '../boostbook.css', - '--stringparam', 'boost.image.src', '../images/bpl.png', - '--stringparam', 'boost.graphics.root', '../images/']) - -env.BoostRST('html/article.html', 'article.rst', resources=['rst.css']) - -if env['NUMPY']: - env.BoostSphinx('html/numpy', 'numpy/') diff --git a/doc/fabscript b/doc/fabscript new file mode 100644 index 0000000000..bd8be7a363 --- /dev/null +++ b/doc/fabscript @@ -0,0 +1,83 @@ +# -*- python -*- +# +# Copyright (c) 2016 Stefan Seefeld +# All rights reserved. +# +# 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) + +from faber.tools.xslt import xsltflags +from faber.tools.boost import quickbook, boostbook +from faber.artefacts import html +from glob import glob as G +from os import makedirs +from os.path import relpath, dirname, exists +from shutil import copyfile + + +def glob(pattern): + prefix = srcdir + '/' + p = len(prefix)+1 + return [f[p:] for f in G(prefix + pattern)] + + +class make_html(action): + + def __init__(self): + action.__init__(self, 'make_html', self.process) + + def map(self, fs): + return boostbook.html.map(fs) + + def process(self, target, source): + boostbook.html(target, source[0:1]) + for s in source[1:]: + t = target[0]._filename + relpath(s._filename, srcdir) + d = dirname(t) + if not exists(d): + makedirs(d) + copyfile(s._filename, t) + + +sphinx_build = action('sphinx-build', 'sphinx-build -b html $(>) $(<)') +rst2html = action('rst2html', 'rst2html --trim-footnote-reference-space --footnote-references=superscript --stylesheet=$(>:D)/rst.css $(>) $(<)') + +python_bbk = rule(quickbook.process, 'python.bbk', 'python.qbk', + dependencies=['release_notes.qbk', + 'building.qbk', + 'configuration.qbk', + 'suport.qbk', + 'faq.qbk', + 'glossary.qbk']) +tutorial_bbk = rule(quickbook.process, 'tutorial.bbk', 'tutorial.qbk') +reference_bbk = rule(quickbook.process, 'reference.bbk', 'reference.qbk') + +python_db = rule(boostbook.db, 'python.db', python_bbk) +tutorial_db = rule(boostbook.db, 'tutorial.db', tutorial_bbk) +reference_db = rule(boostbook.db, 'reference.db', reference_bbk) + +python = html.dir(make_html(), 'html', [python_db, 'boostbook.css'] + glob('/images/*.*') + glob('/images/callouts/*.*'), + features=xsltflags('--stringparam generate.toc "library nop; chaper toc; section toc;"', + '--stringparam html.stylesheet boostbook.css', + '--stringparam boost.image.src images/bpl.png', + '--stringparam boost.graphics.root images/', + '--stringparam boost.defaults none', + '--param toc.max.depth 3', + '--param toc.section.depth 2', + '--param chunk.section.depth 1')) +tutorial = html.dir(boostbook.html, 'html/tutorial', tutorial_db, dependencies=[python], + features=xsltflags('--stringparam html.stylesheet ../boostbook.css', + '--stringparam boost.image.src ../images/bpl.png', + '--stringparam boost.graphics.root ../images/')) +reference = html.dir(boostbook.html, 'html/reference', reference_db, dependencies=[python], + features=xsltflags('--stringparam html.stylesheet ../boostbook.css', + '--stringparam boost.image.src ../images/bpl.png', + '--stringparam boost.graphics.root ../images/')) +numpy = rule(sphinx_build, 'html/numpy', 'numpy', attrs=always, dependencies=[python]) + +article = rule(rst2html, 'html/article.html', 'article.rst') + +html = alias('html', [python, tutorial, reference, numpy, article]) + +default = html diff --git a/doc/numpy/_static/boost.css b/doc/numpy/_static/boost.css index 28f8935991..36c1efd082 100644 --- a/doc/numpy/_static/boost.css +++ b/doc/numpy/_static/boost.css @@ -714,3 +714,23 @@ span.purple { color: purple; } span.gold { color: gold; } span.silver { color: silver; } /* lighter gray */ span.gray { color: #808080; } /* light gray */ + +/* 2022 fix */ + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + diff --git a/doc/numpy/_templates/layout.html b/doc/numpy/_templates/layout.html index 1aa68f0ea1..69e1a868c0 100644 --- a/doc/numpy/_templates/layout.html +++ b/doc/numpy/_templates/layout.html @@ -49,6 +49,9 @@ {%- for scriptfile in script_files %} {%- endfor %} + + + {%- if use_opensearch %}

C++ Boost

+ alt="C++ Boost" src="{{ pathto('_static/bpl.png', 1) }}" border="0"> diff --git a/doc/numpy/conf.py b/doc/numpy/conf.py index 2f5d5e8146..23ab678d3a 100644 --- a/doc/numpy/conf.py +++ b/doc/numpy/conf.py @@ -40,8 +40,8 @@ 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 @@ # 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 @@ # 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) ] diff --git a/doc/numpy/tutorial/dtype.rst b/doc/numpy/tutorial/dtype.rst index 557e72ba2d..9bea646a65 100644 --- a/doc/numpy/tutorial/dtype.rst +++ b/doc/numpy/tutorial/dtype.rst @@ -48,7 +48,7 @@ Next, create a list, and add this tuple to the list. Then use the list to create list_for_dtype.append(for_custom_dtype) ; np::dtype custom_dtype = np::dtype(list_for_dtype) ; -We are now ready to create an ndarray with dimensions specified by \*shape\* and of custom dtpye :: +We are now ready to create an ndarray with dimensions specified by \*shape\* and of custom dtype :: np::ndarray new_array = np::zeros(shape,custom_dtype); } diff --git a/doc/python.qbk b/doc/python.qbk index ffd2d0ddb5..b8b0926def 100644 --- a/doc/python.qbk +++ b/doc/python.qbk @@ -43,6 +43,7 @@ The development of these features was funded in part by grants to `Boost Consult [section Contents] +* [link rn Release Notes] * _tutorial_ * [link building Building and Testing] * _reference_ @@ -58,6 +59,7 @@ The development of these features was funded in part by grants to `Boost Consult [@article.html Building Hybrid Systems With Boost Python], by Dave Abrahams and Ralf W. Grosse-Kunstleve +[include release_notes.qbk] [include building.qbk] [include configuration.qbk] [include support.qbk] diff --git a/doc/reference/call_method.qbk b/doc/reference/call_method.qbk index 926763cebc..fcf68667d0 100644 --- a/doc/reference/call_method.qbk +++ b/doc/reference/call_method.qbk @@ -20,6 +20,8 @@ C++ Module Definition `` #include #include +#include +#include #include #include @@ -28,7 +30,7 @@ class Base { public: virtual char const* class_name() const { return "Base"; } - virtual ~Base(); + virtual ~Base() {}; }; bool is_base(Base* b) @@ -56,7 +58,7 @@ BOOST_PYTHON_MODULE(my_module) { def("is_base", is_base); - class_("Base") + class_("Base") .def("class_name", &Base_callback::Base_name) ; diff --git a/doc/reference/operators.qbk b/doc/reference/operators.qbk index 892d33b7d2..8276f68190 100644 --- a/doc/reference/operators.qbk +++ b/doc/reference/operators.qbk @@ -86,8 +86,8 @@ The column of Python Expressions illustrates the expressions that will be suppor [[C++ Expression][Python Method Name][C++ Implementation][Python Expression (primary, secondary)]] [[`self == r`][`__eq__`][`x == y`][`x == y`, `y == x`]] [[`l == self`][`__eq__`][`y == x`][`y == x`, `x == y`]] -[[`self != r`][`__nq__`][`x != y`][`x != y`, `y != x`]] -[[`l != self`][`__nq__`][`y != x`][`y != x`, `x != y`]] +[[`self != r`][`__ne__`][`x != y`][`x != y`, `y != x`]] +[[`l != self`][`__ne__`][`y != x`][`y != x`, `x != y`]] [[`self < r`][`__lt__`][`x < y`][`x < y`, `y > x`]] [[`l < self`][`__gt__`][`y < x`][`y > x`, `x < y`]] [[`self > r`][`__gt__`][`x > y`][`x > y`, `y < x`]] diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk new file mode 100644 index 0000000000..7e0c82fdfd --- /dev/null +++ b/doc/release_notes.qbk @@ -0,0 +1,11 @@ +[chapter Release Notes + [quickbook 1.7] + [id rn] +] + +[section Version 1.67] + +* The Boost.Python library names now contain the Python version suffix. + A variant compiled with Python 2.7 will thus produce library names + `boost_python27` and `boost_numpy27`, etc., making it possible to host + variants for multiple Python versions next to each other. \ No newline at end of file diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index b3dcfc9434..197470013e 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -117,11 +117,11 @@ 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. -The comments contained in the Jamrules file above should be sufficient +The comments contained in the Jamroot file above should be sufficient to get you going. [h2 Running bjam] @@ -1871,36 +1871,6 @@ This technique has several advantages: * Minimize the need to recompile * Rapid prototyping (you can move the code to C++ if required without changing the interface) -You can even add a little syntactic sugar with the use of metaclasses. Let's -create a special metaclass that "injects" methods in other classes. - - # The one Boost.Python uses for all wrapped classes. - # You can use here any class exported by Boost instead of "point" - BoostPythonMetaclass = point.__class__ - - class injector(object): - class __metaclass__(BoostPythonMetaclass): - def __init__(self, name, bases, dict): - for b in bases: - if type(b) not in (self, type): - for k,v in dict.items(): - setattr(b,k,v) - return type.__init__(self, name, bases, dict) - - # inject some methods in the point foo - class more_point(injector, point): - def __repr__(self): - return 'Point(x=%s, y=%s)' % (self.x, self.y) - def foo(self): - print 'foo!' - -Now let's see how it got: - - >>> print point() - Point(x=10, y=10) - >>> point().foo() - foo! - Another useful idea is to replace constructors with factory functions: _point = point diff --git a/example/README.md b/example/README.md index b090cbe1e3..af03f20ba8 100644 --- a/example/README.md +++ b/example/README.md @@ -3,7 +3,7 @@ # Examples This directory contains various examples using Boost.Python. -You may compile these using the `bjam` command either in this directory +You may compile these using the `b2` command either in this directory or in any of the subdirectories. You may need to adjust the paths in the Jamroot file if Boost.Python is not installed in a default location. diff --git a/example/numpy/demo_gaussian.py b/example/numpy/demo_gaussian.py index 0b1c78995e..08bb58b82a 100644 --- a/example/numpy/demo_gaussian.py +++ b/example/numpy/demo_gaussian.py @@ -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 @@ 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)) diff --git a/example/quickstart/script.py b/example/quickstart/script.py index c3e034ba84..f360cef2d2 100644 --- a/example/quickstart/script.py +++ b/example/quickstart/script.py @@ -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) diff --git a/example/quickstart/test_extending.py b/example/quickstart/test_extending.py index 938c7b9047..035ca96134 100644 --- a/example/quickstart/test_extending.py +++ b/example/quickstart/test_extending.py @@ -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) diff --git a/example/tutorial/hello.py b/example/tutorial/hello.py index 31f75565df..7888b2e0fd 100755 --- a/example/tutorial/hello.py +++ b/example/tutorial/hello.py @@ -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) diff --git a/fabscript b/fabscript index 59be9e1803..5a50615fc8 100644 --- a/fabscript +++ b/fabscript @@ -16,10 +16,12 @@ 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) python = python.instance() +py_suffix = '{}{}'.format(*python.version.split('.')[:2]) features |= set(python.include, python.linkpath, python.libs) class has_numpy(try_run): @@ -74,7 +76,8 @@ checks = [cxx_checks.has_cxx11(features, define('HAS_CXX11')), has_numpy(features)] config = report('config', checks) -src = module('src', features=config.use) -test = module('test', features=config.use) +src = module('src', features=features|config.use) +test = module('test', features=features|config.use) +doc = module('doc', features=features|config.use) default = src.default diff --git a/include/boost/python/call.hpp b/include/boost/python/call.hpp index 5d2d7d2341..c057ee9a12 100644 --- a/include/boost/python/call.hpp +++ b/include/boost/python/call.hpp @@ -60,7 +60,7 @@ call(PyObject* callable ) { PyObject* const result = - PyEval_CallFunction( + PyObject_CallFunction( callable , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil) @@ -69,7 +69,7 @@ call(PyObject* callable // This conversion *must not* be done in the same expression as // the call, because, in the special case where the result is a // reference a Python object which was created by converting a C++ - // argument for passing to PyEval_CallFunction, its reference + // argument for passing to PyObject_CallFunction, its reference // count will be 2 until the end of the full expression containing // the conversion, and that interferes with dangling // pointer/reference detection. diff --git a/include/boost/python/call_method.hpp b/include/boost/python/call_method.hpp index 410f66820e..2f360791d7 100644 --- a/include/boost/python/call_method.hpp +++ b/include/boost/python/call_method.hpp @@ -59,7 +59,7 @@ call_method(PyObject* self, char const* name ) { PyObject* const result = - PyEval_CallMethod( + PyObject_CallMethod( self , const_cast(name) , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") @@ -69,7 +69,7 @@ call_method(PyObject* self, char const* name // This conversion *must not* be done in the same expression as // the call, because, in the special case where the result is a // reference a Python object which was created by converting a C++ - // argument for passing to PyEval_CallFunction, its reference + // argument for passing to PyObject_CallFunction, its reference // count will be 2 until the end of the full expression containing // the conversion, and that interferes with dangling // pointer/reference detection. diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 77f915ba0a..0f1c0fdc1a 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -372,10 +372,11 @@ class class_ : public objects::class_base { typedef typename api::is_object_operators::type is_obj_or_proxy; - return this->make_fn_impl( + return objects::add_doc( + this->make_fn_impl( detail::unwrap_wrapper((W*)0) , f, is_obj_or_proxy(), (char*)0, detail::is_data_member_pointer() - ); + ), NULL); } template @@ -383,10 +384,11 @@ class class_ : public objects::class_base { typedef typename api::is_object_operators::type is_obj_or_proxy; - return this->make_fn_impl( + return objects::add_doc( + this->make_fn_impl( detail::unwrap_wrapper((W*)0) , f, is_obj_or_proxy(), (int*)0, detail::is_data_member_pointer() - ); + ), NULL); } template diff --git a/include/boost/python/converter/obj_mgr_arg_from_python.hpp b/include/boost/python/converter/obj_mgr_arg_from_python.hpp index cd4e1e0ea8..5132804082 100644 --- a/include/boost/python/converter/obj_mgr_arg_from_python.hpp +++ b/include/boost/python/converter/obj_mgr_arg_from_python.hpp @@ -81,9 +81,9 @@ inline object_manager_ref_arg_from_python::object_manager_ref_arg_from_pyth # if defined(__EDG_VERSION__) && __EDG_VERSION__ <= 243 // needed for warning suppression python::detail::borrowed_reference x_ = python::detail::borrowed_reference(x); - python::detail::construct_referent(&m_result.bytes, x_); + python::detail::construct_referent(m_result.bytes, x_); # else - python::detail::construct_referent(&m_result.bytes, (python::detail::borrowed_reference)x); + python::detail::construct_referent(m_result.bytes, (python::detail::borrowed_reference)x); # endif } diff --git a/include/boost/python/converter/pytype_function.hpp b/include/boost/python/converter/pytype_function.hpp index 8e0a4e7995..d072b55fb3 100644 --- a/include/boost/python/converter/pytype_function.hpp +++ b/include/boost/python/converter/pytype_function.hpp @@ -9,7 +9,7 @@ # include # include # include - +# include namespace boost { namespace python { @@ -46,6 +46,12 @@ inline python::type_info unwind_type_id_(boost::type* = 0, mpl::false_ * =0) return boost::python::detail::unwind_type (); } +template +inline python::type_info unwind_type_id_(boost::type >* = 0, mpl::false_ * =0) +{ + return boost::python::detail::unwind_type (); +} + inline python::type_info unwind_type_id_(boost::type* = 0, mpl::true_* =0) { return type_id(); diff --git a/include/boost/python/converter/rvalue_from_python_data.hpp b/include/boost/python/converter/rvalue_from_python_data.hpp index acb38f8498..d728681b3e 100644 --- a/include/boost/python/converter/rvalue_from_python_data.hpp +++ b/include/boost/python/converter/rvalue_from_python_data.hpp @@ -9,6 +9,7 @@ # include # include # include +# include # include # include @@ -132,7 +133,13 @@ template inline rvalue_from_python_data::~rvalue_from_python_data() { if (this->stage1.convertible == this->storage.bytes) - python::detail::destroy_referent(this->storage.bytes); + { + size_t allocated = sizeof(this->storage); + void *ptr = this->storage.bytes; + void *aligned_storage = + ::boost::alignment::align(boost::python::detail::alignment_of::value, 0, ptr, allocated); + python::detail::destroy_referent(aligned_storage); + } } }}} // namespace boost::python::converter diff --git a/include/boost/python/converter/shared_ptr_from_python.hpp b/include/boost/python/converter/shared_ptr_from_python.hpp index bb2ae863ff..b5c62ba940 100644 --- a/include/boost/python/converter/shared_ptr_from_python.hpp +++ b/include/boost/python/converter/shared_ptr_from_python.hpp @@ -49,13 +49,17 @@ struct shared_ptr_from_python new (storage) SP(); else { - SP hold_convertible_ref_count( - (void*)0, shared_ptr_deleter(handle<>(borrowed(source))) ); - // use aliasing constructor - new (storage) SP(hold_convertible_ref_count, - static_cast(data->convertible)); + void *const storage = ((converter::rvalue_from_python_storage >*)data)->storage.bytes; + // Deal with the "None" case. + if (data->convertible == source) + new (storage) SP(); + else + { + SP hold_convertible_ref_count((void*)0, shared_ptr_deleter(handle<>(borrowed(source))) ); + // use aliasing constructor + new (storage) SP(hold_convertible_ref_count, static_cast(data->convertible)); + } } - data->convertible = storage; } }; diff --git a/include/boost/python/def_visitor.hpp b/include/boost/python/def_visitor.hpp index 9c8907cd6f..18dd928684 100644 --- a/include/boost/python/def_visitor.hpp +++ b/include/boost/python/def_visitor.hpp @@ -16,7 +16,7 @@ template class class_; class def_visitor_access { # if defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) \ - || BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x551)) + || BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x551)) // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. public: @@ -52,7 +52,7 @@ class def_visitor friend class def_visitor_access; # if defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) \ - || BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x551)) + || BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x551)) // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. public: diff --git a/include/boost/python/detail/caller.hpp b/include/boost/python/detail/caller.hpp index 1bd30bfb5a..2834d6da99 100644 --- a/include/boost/python/detail/caller.hpp +++ b/include/boost/python/detail/caller.hpp @@ -109,6 +109,23 @@ struct converter_target_type return 0; } }; + +// Generation of ret moved from caller_arity::impl::signature to here due to "feature" in MSVC 15.7.2 with /O2 +// which left the ret uninitialized and caused segfaults in Python interpreter. +template const signature_element* get_ret() +{ + typedef BOOST_DEDUCED_TYPENAME Policies::template extract_return_type::type rtype; + typedef typename select_result_converter::type result_converter; + + static const signature_element ret = { + (is_void::value ? "void" : type_id().name()) + , &detail::converter_target_type::get_pytype + , boost::detail::indirect_traits::is_reference_to_non_const::value + }; + + return &ret; +} + #endif @@ -229,16 +246,12 @@ struct caller_arity { const signature_element * sig = detail::signature::elements(); #ifndef BOOST_PYTHON_NO_PY_SIGNATURES + // MSVC 15.7.2, when compiling to /O2 left the static const signature_element ret, + // originally defined here, uninitialized. This in turn led to SegFault in Python interpreter. + // Issue is resolved by moving the generation of ret to separate function in detail namespace (see above). + const signature_element * ret = detail::get_ret(); - typedef BOOST_DEDUCED_TYPENAME Policies::template extract_return_type::type rtype; - typedef typename select_result_converter::type result_converter; - - static const signature_element ret = { - (is_void::value ? "void" : type_id().name()) - , &detail::converter_target_type::get_pytype - , boost::detail::indirect_traits::is_reference_to_non_const::value - }; - py_func_sig_info res = {sig, &ret }; + py_func_sig_info res = {sig, ret }; #else py_func_sig_info res = {sig, sig }; #endif diff --git a/include/boost/python/detail/config.hpp b/include/boost/python/detail/config.hpp index 3e4b7c9e54..e2ac827040 100644 --- a/include/boost/python/detail/config.hpp +++ b/include/boost/python/detail/config.hpp @@ -57,9 +57,15 @@ ****************************************************************************/ // backwards compatibility: -#ifdef BOOST_PYTHON_STATIC_LIB -# define BOOST_PYTHON_STATIC_LINK -# elif !defined(BOOST_PYTHON_DYNAMIC_LIB) +#if defined(BOOST_PYTHON_STATIC_LINK) && !defined(BOOST_PYTHON_STATIC_LIB) +# define BOOST_PYTHON_STATIC_LIB +#endif + +#if defined(BOOST_PYTHON_DYNAMIC_LINK) && !defined(BOOST_PYTHON_DYNAMIC_LIB) +# define BOOST_PYTHON_DYNAMIC_LIB +#endif + +#if !defined(BOOST_PYTHON_STATIC_LIB) && !defined(BOOST_PYTHON_DYNAMIC_LIB) # define BOOST_PYTHON_DYNAMIC_LIB #endif @@ -105,11 +111,9 @@ // Set the name of our library, this will get undef'ed by auto_link.hpp // once it's done with it: // -#if PY_MAJOR_VERSION == 2 -# define BOOST_LIB_NAME boost_python -#elif PY_MAJOR_VERSION == 3 -# define BOOST_LIB_NAME boost_python3 -#endif +#define _BOOST_PYTHON_CONCAT(N, M, m) N ## M ## m +#define BOOST_PYTHON_CONCAT(N, M, m) _BOOST_PYTHON_CONCAT(N, M, m) +#define BOOST_LIB_NAME BOOST_PYTHON_CONCAT(boost_python, PY_MAJOR_VERSION, PY_MINOR_VERSION) // // If we're importing code from a dll, then tell auto_link.hpp about it: // @@ -122,6 +126,9 @@ #include #endif // auto-linking disabled +#undef BOOST_PYTHON_CONCAT +#undef _BOOST_PYTHON_CONCAT + #ifndef BOOST_PYTHON_NO_PY_SIGNATURES #define BOOST_PYTHON_SUPPORTS_PY_SIGNATURES // enables smooth transition #endif diff --git a/include/boost/python/detail/is_auto_ptr.hpp b/include/boost/python/detail/is_auto_ptr.hpp index 3b8198b8dd..36affcd215 100644 --- a/include/boost/python/detail/is_auto_ptr.hpp +++ b/include/boost/python/detail/is_auto_ptr.hpp @@ -8,6 +8,8 @@ # ifndef BOOST_NO_AUTO_PTR # include # include +# else +# include # endif namespace boost { namespace python { namespace detail { diff --git a/include/boost/python/detail/pymutex.hpp b/include/boost/python/detail/pymutex.hpp new file mode 100644 index 0000000000..2d2e2d6266 --- /dev/null +++ b/include/boost/python/detail/pymutex.hpp @@ -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 +#ifdef Py_GIL_DISABLED +// needed for pymutex wrapper +#include +#include +#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 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 diff --git a/include/boost/python/detail/referent_storage.hpp b/include/boost/python/detail/referent_storage.hpp index 2cddf696d5..f646d2ae1d 100644 --- a/include/boost/python/detail/referent_storage.hpp +++ b/include/boost/python/detail/referent_storage.hpp @@ -5,39 +5,21 @@ #ifndef REFERENT_STORAGE_DWA200278_HPP # define REFERENT_STORAGE_DWA200278_HPP # include +# include # include namespace boost { namespace python { namespace detail { -struct alignment_dummy; -typedef void (*function_ptr)(); -typedef int (alignment_dummy::*member_ptr); -typedef int (alignment_dummy::*member_function_ptr)(); - -# define BOOST_PYTHON_ALIGNER(T, n) \ - typename mpl::if_c< \ - sizeof(T) <= size, T, char>::type t##n - -// Storage for size bytes, aligned to all fundamental types no larger than size -template -union aligned_storage +template +struct aligned_storage { - BOOST_PYTHON_ALIGNER(char, 0); - BOOST_PYTHON_ALIGNER(short, 1); - BOOST_PYTHON_ALIGNER(int, 2); - BOOST_PYTHON_ALIGNER(long, 3); - BOOST_PYTHON_ALIGNER(float, 4); - BOOST_PYTHON_ALIGNER(double, 5); - BOOST_PYTHON_ALIGNER(long double, 6); - BOOST_PYTHON_ALIGNER(void*, 7); - BOOST_PYTHON_ALIGNER(function_ptr, 8); - BOOST_PYTHON_ALIGNER(member_ptr, 9); - BOOST_PYTHON_ALIGNER(member_function_ptr, 10); + union type + { + typename ::boost::aligned_storage::type data; char bytes[size]; + }; }; - -# undef BOOST_PYTHON_ALIGNER - + // Compute the size of T's referent. We wouldn't need this at all, // but sizeof() is broken in CodeWarriors <= 8.0 template struct referent_size; @@ -50,15 +32,12 @@ union aligned_storage std::size_t, value = sizeof(T)); }; - // A metafunction returning a POD type which can store U, where T == // U&. If T is not a reference type, returns a POD which can store T. template struct referent_storage { - typedef aligned_storage< - ::boost::python::detail::referent_size::value - > type; + typedef typename aligned_storage::value, alignment_of::value>::type type; }; }}} // namespace boost::python::detail diff --git a/include/boost/python/detail/unwind_type.hpp b/include/boost/python/detail/unwind_type.hpp index f6cdab64fe..b81bf7c898 100644 --- a/include/boost/python/detail/unwind_type.hpp +++ b/include/boost/python/detail/unwind_type.hpp @@ -11,13 +11,15 @@ namespace boost { namespace python { namespace detail { -#ifndef _MSC_VER //if forward declared, msvc6.5 does not recognize them as inline -// forward declaration, required (at least) by Tru64 cxx V6.5-042 +#if (!defined(_MSC_VER) || _MSC_VER >= 1915) +// If forward declared, msvc6.5 does not recognize them as inline. +// However, as of msvc14.15 (_MSC_VER 1915/Visual Studio 15.8.0) name lookup is now consistent with other compilers. +// forward declaration, required (at least) by Tru64 cxx V6.5-042 and msvc14.15 template inline typename Generator::result_type unwind_type(U const& p, Generator* = 0); -// forward declaration, required (at least) by Tru64 cxx V6.5-042 +// forward declaration, required (at least) by Tru64 cxx V6.5-042 and msvc14.15 template inline typename Generator::result_type unwind_type(boost::type*p = 0, Generator* = 0); @@ -83,7 +85,7 @@ struct unwind_helper template inline typename Generator::result_type -#ifndef _MSC_VER +#if (!defined(_MSC_VER) || _MSC_VER >= 1915) unwind_type(U const& p, Generator*) #else unwind_type(U const& p, Generator* = 0) @@ -148,7 +150,7 @@ struct unwind_helper2 // why bother? template inline typename Generator::result_type -#ifndef _MSC_VER +#if (!defined(_MSC_VER) || _MSC_VER >= 1915) unwind_type(boost::type*, Generator*) #else unwind_type(boost::type*p =0, Generator* =0) diff --git a/include/boost/python/detail/wrap_python.hpp b/include/boost/python/detail/wrap_python.hpp index 9fdb222c68..037e4bf2ec 100644 --- a/include/boost/python/detail/wrap_python.hpp +++ b/include/boost/python/detail/wrap_python.hpp @@ -47,6 +47,13 @@ # endif #endif +// pyconfig.h defines a macro with hypot name, what breaks libstdc++ math headers +// that Python.h tries to include afterwards. +#if defined(__MINGW32__) +# include +# include +#endif + # include # if defined(_SGI_COMPILER_VERSION) && _SGI_COMPILER_VERSION >= 740 # undef _POSIX_C_SOURCE @@ -83,6 +90,7 @@ // than MSVC on Win32 // #if defined(_WIN32) || defined(__CYGWIN__) + # if defined(__GNUC__) && defined(__CYGWIN__) # if defined(__LP64__) @@ -138,19 +146,45 @@ typedef int pid_t; # undef hypot // undo the evil #define left by Python. -# elif defined(__BORLANDC__) +# elif defined(__BORLANDC__) && !defined(__clang__) # undef HAVE_HYPOT # define HAVE_HYPOT 1 # endif #endif // _WIN32 +#if defined(__GNUC__) +# if defined(__has_warning) +# define BOOST_PYTHON_GCC_HAS_WREGISTER __has_warning("-Wregister") +# else +# define BOOST_PYTHON_GCC_HAS_WREGISTER __GNUC__ >= 7 +# endif +#else +# define BOOST_PYTHON_GCC_HAS_WREGISTER 0 +#endif + +// Python.h header uses `register` keyword until Python 3.4 +#if BOOST_PYTHON_GCC_HAS_WREGISTER +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wregister" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 5033) // 'register' is no longer a supported storage class +#endif + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 2 && PY_MICRO_VERSION < 2 # include #else # include #endif +#if BOOST_PYTHON_GCC_HAS_WREGISTER +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif +#undef BOOST_PYTHON_GCC_HAS_WREGISTER + #ifdef BOOST_PYTHON_ULONG_MAX_UNDEFINED # undef ULONG_MAX # undef BOOST_PYTHON_ULONG_MAX_UNDEFINED @@ -193,7 +227,11 @@ typedef int pid_t; # define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, +#endif +#if PY_VERSION_HEX < 0x030900A4 +# define Py_SET_TYPE(obj, type) ((Py_TYPE(obj) = (type)), (void)0) +# define Py_SET_SIZE(obj, size) ((Py_SIZE(obj) = (size)), (void)0) #endif diff --git a/include/boost/python/exception_translator.hpp b/include/boost/python/exception_translator.hpp index 75ed0e1eec..1aa1465bdf 100644 --- a/include/boost/python/exception_translator.hpp +++ b/include/boost/python/exception_translator.hpp @@ -7,7 +7,7 @@ # include -# include +# include # include # include # include @@ -18,6 +18,7 @@ namespace boost { namespace python { template void register_exception_translator(Translate translate, boost::type* = 0) { + using namespace boost::placeholders; detail::register_exception_handler( boost::bind(detail::translate_exception(), _1, _2, translate) ); diff --git a/include/boost/python/instance_holder.hpp b/include/boost/python/instance_holder.hpp index 933f50d1a1..f4ed1e6608 100644 --- a/include/boost/python/instance_holder.hpp +++ b/include/boost/python/instance_holder.hpp @@ -38,7 +38,7 @@ struct BOOST_PYTHON_DECL instance_holder : private noncopyable // Allocate storage for an object of the given size at the given // offset in the Python instance<> object if bytes are available // there. Otherwise allocate size bytes of heap memory. - static void* allocate(PyObject*, std::size_t offset, std::size_t size); + static void* allocate(PyObject*, std::size_t offset, std::size_t size, std::size_t alignment = 1); // Deallocate storage from the heap if it was not carved out of // the given Python object by allocate(), above. diff --git a/include/boost/python/iterator.hpp b/include/boost/python/iterator.hpp index 7c06ca2320..b0ea578959 100644 --- a/include/boost/python/iterator.hpp +++ b/include/boost/python/iterator.hpp @@ -22,7 +22,7 @@ works correctly. */ # pragma warning(disable: 4180) # endif -# include +# include # include namespace boost { namespace python { @@ -40,6 +40,7 @@ namespace detail , Target&(*)() ) { + using namespace boost::placeholders; return objects::make_iterator_function( boost::protect(boost::bind(get_start, _1)) , boost::protect(boost::bind(get_finish, _1)) diff --git a/include/boost/python/list.hpp b/include/boost/python/list.hpp index 10fd40fda5..0d5e2c8fd9 100644 --- a/include/boost/python/list.hpp +++ b/include/boost/python/list.hpp @@ -73,7 +73,7 @@ class list : public detail::list_base } template - long count(T const& value) const + ssize_t count(T const& value) const { return base::count(object(value)); } diff --git a/include/boost/python/long.hpp b/include/boost/python/long.hpp index 129c61f9e4..c15604c91c 100644 --- a/include/boost/python/long.hpp +++ b/include/boost/python/long.hpp @@ -24,8 +24,8 @@ namespace detail BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_base, object) private: - static detail::new_non_null_reference call(object const&); - static detail::new_non_null_reference call(object const&, object const&); + static detail::new_reference call(object const&); + static detail::new_reference call(object const&, object const&); }; } diff --git a/include/boost/python/make_constructor.hpp b/include/boost/python/make_constructor.hpp index 3ec9ad5f86..3769970cad 100644 --- a/include/boost/python/make_constructor.hpp +++ b/include/boost/python/make_constructor.hpp @@ -61,7 +61,8 @@ namespace detail typedef objects::pointer_holder holder; typedef objects::instance instance_t; - void* memory = holder::allocate(this->m_self, offsetof(instance_t, storage), sizeof(holder)); + void* memory = holder::allocate(this->m_self, offsetof(instance_t, storage), sizeof(holder), + boost::python::detail::alignment_of::value); try { #if defined(BOOST_NO_CXX11_SMART_PTR) (new (memory) holder(x))->install(this->m_self); diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 7fe5a1c8a2..390db82cf4 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -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 +bool gil_not_used_option(F &&, O &&...o); +template +inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) { + return f.flag() || gil_not_used_option(o...); +} +template +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 diff --git a/include/boost/python/numpy/config.hpp b/include/boost/python/numpy/config.hpp index 97178906e0..f70b94cb3e 100644 --- a/include/boost/python/numpy/config.hpp +++ b/include/boost/python/numpy/config.hpp @@ -62,11 +62,9 @@ // Set the name of our library, this will get undef'ed by auto_link.hpp // once it's done with it: // -#if PY_MAJOR_VERSION == 2 -# define BOOST_LIB_NAME boost_numpy -#elif PY_MAJOR_VERSION == 3 -# define BOOST_LIB_NAME boost_numpy3 -#endif +#define _BOOST_PYTHON_CONCAT(N, M, m) N ## M ## m +#define BOOST_PYTHON_CONCAT(N, M, m) _BOOST_PYTHON_CONCAT(N, M, m) +#define BOOST_LIB_NAME BOOST_PYTHON_CONCAT(boost_numpy, PY_MAJOR_VERSION, PY_MINOR_VERSION) // // If we're importing code from a dll, then tell auto_link.hpp about it: // @@ -79,6 +77,9 @@ #include #endif // auto-linking disabled +#undef BOOST_PYTHON_CONCAT +#undef _BOOST_PYTHON_CONCAT + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #endif // CONFIG_NUMPY20170215_H_ diff --git a/include/boost/python/numpy/dtype.hpp b/include/boost/python/numpy/dtype.hpp index 4673745e57..9438d79fdc 100644 --- a/include/boost/python/numpy/dtype.hpp +++ b/include/boost/python/numpy/dtype.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace boost { namespace python { namespace numpy { diff --git a/include/boost/python/object/add_to_namespace.hpp b/include/boost/python/object/add_to_namespace.hpp index 9f4167d6d2..e81186790a 100644 --- a/include/boost/python/object/add_to_namespace.hpp +++ b/include/boost/python/object/add_to_namespace.hpp @@ -18,6 +18,8 @@ BOOST_PYTHON_DECL void add_to_namespace( BOOST_PYTHON_DECL void add_to_namespace( object const& name_space, char const* name, object const& attribute, char const* doc); +BOOST_PYTHON_DECL object const& add_doc(object const& attribute, char const* doc); + }}} // namespace boost::python::objects #endif // ADD_TO_NAMESPACE_DWA200286_HPP diff --git a/include/boost/python/object/function.hpp b/include/boost/python/object/function.hpp index f29d344820..ec1fc3d38b 100644 --- a/include/boost/python/object/function.hpp +++ b/include/boost/python/object/function.hpp @@ -35,6 +35,8 @@ struct BOOST_PYTHON_DECL function : PyObject static void add_to_namespace( object const& name_space, char const* name, object const& attribute, char const* doc); + static object const& add_doc(object const& attribute, char const* doc); + object const& doc() const; void doc(object const& x); @@ -42,6 +44,8 @@ struct BOOST_PYTHON_DECL function : PyObject object const& get_namespace() const { return m_namespace; } + object const& get_module() const { return m_module; } + private: // helper functions object signature(bool show_return_type=false) const; object signatures(bool show_return_type=false) const; @@ -53,6 +57,7 @@ struct BOOST_PYTHON_DECL function : PyObject handle m_overloads; object m_name; object m_namespace; + object m_module; object m_doc; object m_arg_names; unsigned m_nkeyword_values; diff --git a/include/boost/python/object/function_doc_signature.hpp b/include/boost/python/object/function_doc_signature.hpp index 4f00cb385a..91c90895ca 100644 --- a/include/boost/python/object/function_doc_signature.hpp +++ b/include/boost/python/object/function_doc_signature.hpp @@ -18,13 +18,13 @@ namespace boost { namespace python { namespace objects { class function_doc_signature_generator{ - static const char * py_type_str(const python::detail::signature_element &s); + static str py_type_str(const python::detail::signature_element &s, const object& current_module_name); static bool arity_cmp( function const *f1, function const *f2 ); static bool are_seq_overloads( function const *f1, function const *f2 , bool check_docs); static std::vector flatten(function const *f); static std::vector split_seq_overloads( const std::vector &funcs, bool split_on_doc_change); static str raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types = false); - static str parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types); + static str parameter_string(py_function const &f, size_t n, object arg_names, const object& module_name, bool cpp_types); static str pretty_signature(function const *f, size_t n_overloads, bool cpp_types = false); public: diff --git a/include/boost/python/object/instance.hpp b/include/boost/python/object/instance.hpp index 27b91a1e5f..ee4a6c5822 100644 --- a/include/boost/python/object/instance.hpp +++ b/include/boost/python/object/instance.hpp @@ -6,7 +6,7 @@ # define INSTANCE_DWA200295_HPP # include - +# include # include namespace boost { namespace python @@ -28,7 +28,7 @@ struct instance typedef typename boost::python::detail::type_with_alignment< boost::python::detail::alignment_of::value >::type align_t; - + union { align_t align; @@ -41,9 +41,10 @@ struct additional_instance_size { typedef instance instance_data; typedef instance instance_char; - BOOST_STATIC_CONSTANT( - std::size_t, value = sizeof(instance_data) - - BOOST_PYTHON_OFFSETOF(instance_char,storage)); + BOOST_STATIC_CONSTANT(std::size_t, + value = sizeof(instance_data) - + BOOST_PYTHON_OFFSETOF(instance_char,storage) + + boost::python::detail::alignment_of::value); }; }}} // namespace boost::python::object diff --git a/include/boost/python/object/iterator.hpp b/include/boost/python/object/iterator.hpp index e2f65721fb..874950365d 100644 --- a/include/boost/python/object/iterator.hpp +++ b/include/boost/python/object/iterator.hpp @@ -25,7 +25,7 @@ # include -# include +# include namespace boost { namespace python { namespace objects { @@ -42,7 +42,7 @@ struct iterator_range { iterator_range(object sequence, Iterator start, Iterator finish); - typedef boost::detail::iterator_traits traits_t; + typedef std::iterator_traits traits_t; struct next { diff --git a/include/boost/python/object/make_holder.hpp b/include/boost/python/object/make_holder.hpp index 0d54dd9f66..735e5395ca 100644 --- a/include/boost/python/object/make_holder.hpp +++ b/include/boost/python/object/make_holder.hpp @@ -89,8 +89,9 @@ struct make_holder BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, t, a)) { typedef instance instance_t; - - void* memory = Holder::allocate(p, offsetof(instance_t, storage), sizeof(Holder)); + + void* memory = Holder::allocate(p, offsetof(instance_t, storage), sizeof(Holder), + boost::python::detail::alignment_of::value); try { (new (memory) Holder( p BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_DO_FORWARD_ARG, nil)))->install(p); diff --git a/include/boost/python/object/make_instance.hpp b/include/boost/python/object/make_instance.hpp index 31ec08f7c3..713fdc5ecd 100644 --- a/include/boost/python/object/make_instance.hpp +++ b/include/boost/python/object/make_instance.hpp @@ -43,11 +43,14 @@ struct make_instance_impl // construct the new C++ object and install the pointer // in the Python object. - Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result); + Holder *holder =Derived::construct(instance->storage.bytes, (PyObject*)instance, x); + holder->install(raw_result); // Note the position of the internally-stored Holder, // for the sake of destruction - Py_SIZE(instance) = offsetof(instance_t, storage); + const size_t offset = reinterpret_cast(holder) - + reinterpret_cast(instance->storage.bytes) + offsetof(instance_t, storage); + Py_SET_SIZE(instance, offset); // Release ownership of the python object protect.cancel(); @@ -69,7 +72,10 @@ struct make_instance static inline Holder* construct(void* storage, PyObject* instance, reference_wrapper x) { - return new (storage) Holder(instance, x); + size_t allocated = objects::additional_instance_size::value; + void* aligned_storage = ::boost::alignment::align(boost::python::detail::alignment_of::value, + sizeof(Holder), storage, allocated); + return new (aligned_storage) Holder(instance, x); } }; diff --git a/include/boost/python/object_core.hpp b/include/boost/python/object_core.hpp index 16480d0d89..074360d415 100644 --- a/include/boost/python/object_core.hpp +++ b/include/boost/python/object_core.hpp @@ -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); } diff --git a/include/boost/python/object_operators.hpp b/include/boost/python/object_operators.hpp index d436bb0144..45d6d028cc 100644 --- a/include/boost/python/object_operators.hpp +++ b/include/boost/python/object_operators.hpp @@ -9,7 +9,7 @@ # include # include -# include +# include # include # include @@ -40,7 +40,7 @@ struct is_object_operators # if !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_IS_CONVERTIBLE) template struct enable_binary - : boost::iterators::enable_if, T> + : boost::enable_if_::value, T> {}; # define BOOST_PYTHON_BINARY_RETURN(T) typename enable_binary::type # else diff --git a/include/boost/python/other.hpp b/include/boost/python/other.hpp index 24a24ad8d1..26ebb426ba 100644 --- a/include/boost/python/other.hpp +++ b/include/boost/python/other.hpp @@ -1,5 +1,5 @@ -#ifndef OTHER_DWA20020601_HPP -# define OTHER_DWA20020601_HPP +#ifndef BOOST_PYTHON_OTHER_HPP +# define BOOST_PYTHON_OTHER_HPP # include // Copyright David Abrahams 2002. @@ -7,8 +7,6 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -# pragma once - # include namespace boost { namespace python { @@ -51,4 +49,4 @@ namespace detail }} // namespace boost::python -#endif // #ifndef OTHER_DWA20020601_HPP +#endif diff --git a/include/boost/python/override.hpp b/include/boost/python/override.hpp index 39714257f9..b631226fd6 100644 --- a/include/boost/python/override.hpp +++ b/include/boost/python/override.hpp @@ -97,7 +97,7 @@ class override : public object operator()() const { detail::method_result x( - PyEval_CallFunction( + PyObject_CallFunction( this->ptr() , const_cast("()") )); @@ -132,7 +132,7 @@ detail::method_result operator()( BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, const& a) ) const { detail::method_result x( - PyEval_CallFunction( + PyObject_CallFunction( this->ptr() , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_fast_arg_to_python_get, nil) diff --git a/include/boost/python/ptr.hpp b/include/boost/python/ptr.hpp index 287daba458..8e97aa4064 100644 --- a/include/boost/python/ptr.hpp +++ b/include/boost/python/ptr.hpp @@ -1,5 +1,5 @@ -#ifndef PTR_DWA20020601_HPP -# define PTR_DWA20020601_HPP +#ifndef BOOST_PYTHON_PTR_HPP +# define BOOST_PYTHON_PTR_HPP # include // Copyright David Abrahams 2002. @@ -11,8 +11,6 @@ // Copyright (C) 1999, 2000 Jaakko Jarvi (jaakko.jarvi@cs.utu.fi) // Copyright (C) 2001 Peter Dimov -# pragma once - # include # include @@ -64,4 +62,4 @@ class unwrap_pointer > }} // namespace boost::python -#endif // #ifndef PTR_DWA20020601_HPP +#endif diff --git a/include/boost/python/pure_virtual.hpp b/include/boost/python/pure_virtual.hpp index 58e9aedef1..f3b298de2c 100644 --- a/include/boost/python/pure_virtual.hpp +++ b/include/boost/python/pure_virtual.hpp @@ -96,6 +96,7 @@ namespace detail , make_function( detail::nullary_function_adaptor(pure_virtual_called) , default_call_policies() + , options.keywords() , detail::error_signature(detail::get_signature(m_pmf)) ) ); diff --git a/include/boost/python/suite/indexing/detail/indexing_suite_detail.hpp b/include/boost/python/suite/indexing/detail/indexing_suite_detail.hpp index eb8b81c0a3..d470e32d77 100644 --- a/include/boost/python/suite/indexing/detail/indexing_suite_detail.hpp +++ b/include/boost/python/suite/indexing/detail/indexing_suite_detail.hpp @@ -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"); diff --git a/meta/libraries.json b/meta/libraries.json index e2f2a05472..80f20f846c 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -10,5 +10,6 @@ ], "maintainers": [ "Stefan Seefeld " - ] + ], + "cxxstd": "03" } diff --git a/src/SConscript b/src/SConscript deleted file mode 100644 index 6d2e5694b9..0000000000 --- a/src/SConscript +++ /dev/null @@ -1,61 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -Import('env') - -env.AppendUnique(CPPDEFINES = ["${LINK_DYNAMIC and 'BOOST_PYTHON_DYN_LINK=1' or ''}"]) - -env1 = env.Clone() -env1.AppendUnique(CPPDEFINES = ['BOOST_PYTHON_SOURCE']) - -env1.BoostLibrary( - 'python', - ['list.cpp', - 'long.cpp', - 'dict.cpp', - 'tuple.cpp', - 'str.cpp', - 'slice.cpp', - 'converter/from_python.cpp', - 'converter/registry.cpp', - 'converter/type_id.cpp', - 'object/enum.cpp', - 'object/class.cpp', - 'object/function.cpp', - 'object/inheritance.cpp', - 'object/life_support.cpp', - 'object/pickle_support.cpp', - 'errors.cpp', - 'module.cpp', - 'converter/builtin_converters.cpp', - 'converter/arg_to_python_base.cpp', - 'object/iterator.cpp', - 'object/stl_iterator.cpp', - 'object_protocol.cpp', - 'object_operators.cpp', - 'wrapper.cpp', - 'import.cpp', - 'exec.cpp', - 'object/function_doc_signature.cpp']) - -if env['NUMPY']: - env2 = env.Clone() - env2.Append(CPPPATH=env['NUMPY_CPPPATH']) - build_dir = env.Dir('$BOOST_CURRENT_VARIANT_DIR/src') - env2.AppendUnique(CPPDEFINES = ['BOOST_NUMPY_SOURCE'], - LIBPATH = [build_dir], - LIBS='boost_python' + env["BOOST_SUFFIX"]) - env2.BoostLibrary( - 'numpy', - ['numpy/dtype.cpp', - 'numpy/matrix.cpp', - 'numpy/ndarray.cpp', - 'numpy/numpy.cpp', - 'numpy/scalars.cpp', - 'numpy/ufunc.cpp']) diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index 9678be1cb6..53a149fa72 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -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 diff --git a/src/converter/registry.cpp b/src/converter/registry.cpp index aa20c3f685..1b23dbef48 100644 --- a/src/converter/registry.cpp +++ b/src/converter/registry.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -112,9 +113,9 @@ registration::~registration() namespace // { typedef registration entry; - + typedef std::set registry_t; - + #ifndef BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND registry_t& entries() { @@ -181,6 +182,8 @@ namespace // 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 diff --git a/src/converter/type_id.cpp b/src/converter/type_id.cpp index c6a8bf7a04..fafb13619c 100644 --- a/src/converter/type_id.cpp +++ b/src/converter/type_id.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -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 > mangling_map; diff --git a/src/dict.cpp b/src/dict.cpp index 77d840d455..296bc21e9c 100644 --- a/src/dict.cpp +++ b/src/dict.cpp @@ -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 { diff --git a/src/errors.cpp b/src/errors.cpp index 34ea22f43e..7f6b1880d5 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -10,9 +10,21 @@ #include #include #include +#include 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 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 diff --git a/src/exec.cpp b/src/exec.cpp index 171c6f4189..7488da1f6d 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -104,14 +104,22 @@ object BOOST_PYTHON_DECL exec_file(char const *filename, object global, object l if (local.is_none()) local = global; // should be 'char const *' but older python versions don't use 'const' yet. char *f = const_cast(filename); - // Let python open the file to avoid potential binary incompatibilities. -#if PY_VERSION_HEX >= 0x03040000 - FILE *fs = _Py_fopen(f, "r"); +#if PY_VERSION_HEX >= 0x03010000 + // Let python manage any UTF bits to avoid potential incompatibilities. + PyObject *fo = Py_BuildValue("s", f); + PyObject *fb = Py_None; + PyUnicode_FSConverter(fo, &fb); + char *f_as_uft = PyBytes_AsString(fb); + FILE *fs = fopen(f_as_uft, "r"); + Py_DECREF(fo); + Py_DECREF(fb); #elif PY_VERSION_HEX >= 0x03000000 + // Let python open the file to avoid potential binary incompatibilities. PyObject *fo = Py_BuildValue("s", f); - FILE *fs = _Py_fopen(fo, "r"); + FILE *fs = fopen(fo, "r"); Py_DECREF(fo); #else + // Let python open the file to avoid potential binary incompatibilities. PyObject *pyfile = PyFile_FromString(f, const_cast("r")); if (!pyfile) throw std::invalid_argument(std::string(f) + " : no such file"); python::handle<> file(pyfile); @@ -121,6 +129,7 @@ object BOOST_PYTHON_DECL exec_file(char const *filename, object global, object l f, Py_file_input, global.ptr(), local.ptr()); + fclose(fs); if (!result) throw_error_already_set(); return object(detail::new_reference(result)); } diff --git a/src/fabscript b/src/fabscript index d5dbf60312..0ebeac6098 100644 --- a/src/fabscript +++ b/src/fabscript @@ -13,7 +13,7 @@ from faber.tools.compiler import define root = module('..') -bpl = library('boost_python', +bpl = library('boost_python' + root.py_suffix, ['list.cpp', 'long.cpp', 'dict.cpp', @@ -44,7 +44,7 @@ bpl = library('boost_python', dependencies=root.config, features=features + define('BOOST_PYTHON_SOURCE')) -bnl = library('boost_numpy', +bnl = library('boost_numpy' + root.py_suffix, ['numpy/dtype.cpp', 'numpy/matrix.cpp', 'numpy/ndarray.cpp', diff --git a/src/long.cpp b/src/long.cpp index 1ec8ebc011..6aa2965e83 100644 --- a/src/long.cpp +++ b/src/long.cpp @@ -6,16 +6,16 @@ namespace boost { namespace python { namespace detail { -new_non_null_reference long_base::call(object const& arg_) +new_reference long_base::call(object const& arg_) { - return (detail::new_non_null_reference)PyObject_CallFunction( + return (detail::new_reference)PyObject_CallFunction( (PyObject*)&PyLong_Type, const_cast("(O)"), arg_.ptr()); } -new_non_null_reference long_base::call(object const& arg_, object const& base) +new_reference long_base::call(object const& arg_, object const& base) { - return (detail::new_non_null_reference)PyObject_CallFunction( + return (detail::new_reference)PyObject_CallFunction( (PyObject*)&PyLong_Type, const_cast("(OO)"), arg_.ptr(), base.ptr()); } diff --git a/src/module.cpp b/src/module.cpp index 9628481996..c32f4187bc 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -21,7 +21,7 @@ namespace object m_obj(((borrowed_reference_t*)m)); scope current_module(m_obj); - handle_exception(init_function); + if (handle_exception(init_function)) return NULL; } return m; @@ -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); } diff --git a/src/numpy/dtype.cpp b/src/numpy/dtype.cpp index 88a20a27b5..1ce8c6ec32 100644 --- a/src/numpy/dtype.cpp +++ b/src/numpy/dtype.cpp @@ -98,37 +98,18 @@ python::detail::new_reference dtype::convert(object const & arg, bool align) return python::detail::new_reference(reinterpret_cast(obj)); } -int dtype::get_itemsize() const { return reinterpret_cast(ptr())->elsize;} - -bool equivalent(dtype const & a, dtype const & b) { - // On Windows x64, the behaviour described on - // http://docs.scipy.org/doc/numpy/reference/c-api.array.html for - // PyArray_EquivTypes unfortunately does not extend as expected: - // "For example, on 32-bit platforms, NPY_LONG and NPY_INT are equivalent". - // This should also hold for 64-bit platforms (and does on Linux), but not - // on Windows. Implement an alternative: -#ifdef _MSC_VER - if (sizeof(long) == sizeof(int) && - // Manually take care of the type equivalence. - ((a == dtype::get_builtin() || a == dtype::get_builtin()) && - (b == dtype::get_builtin() || b == dtype::get_builtin()) || - (a == dtype::get_builtin() || a == dtype::get_builtin()) && - (b == dtype::get_builtin() || b == dtype::get_builtin()))) { - return true; - } else { - return PyArray_EquivTypes( - reinterpret_cast(a.ptr()), - reinterpret_cast(b.ptr()) - ); - } +int dtype::get_itemsize() const { +#if NPY_ABI_VERSION < 0x02000000 + return reinterpret_cast(ptr())->elsize; #else - return PyArray_EquivTypes( - reinterpret_cast(a.ptr()), - reinterpret_cast(b.ptr()) - ); + return PyDataType_ELSIZE(reinterpret_cast(ptr())); #endif } +bool equivalent(dtype const & a, dtype const & b) { + return a == b; +} + namespace { diff --git a/src/numpy/numpy.cpp b/src/numpy/numpy.cpp index 8e259bc755..3ae2295e39 100644 --- a/src/numpy/numpy.cpp +++ b/src/numpy/numpy.cpp @@ -19,6 +19,7 @@ static void wrap_import_array() static void * wrap_import_array() { import_array(); + return NULL; } #endif diff --git a/src/object/class.cpp b/src/object/class.cpp index aeef688e28..e03d4e009a 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -5,6 +5,7 @@ #include #include // #including this first is an intel6 workaround +#include #include #include @@ -208,7 +209,7 @@ namespace objects { if (static_data_object.tp_dict == 0) { - Py_TYPE(&static_data_object) = &PyType_Type; + Py_SET_TYPE(&static_data_object, &PyType_Type); static_data_object.tp_base = &PyProperty_Type; if (PyType_Ready(&static_data_object)) return 0; @@ -316,7 +317,7 @@ namespace objects { if (class_metatype_object.tp_dict == 0) { - Py_TYPE(&class_metatype_object) = &PyType_Type; + Py_SET_TYPE(&class_metatype_object, &PyType_Type); class_metatype_object.tp_base = &PyType_Type; if (PyType_Ready(&class_metatype_object)) return type_handle(); @@ -332,8 +333,9 @@ namespace objects for (instance_holder* p = kill_me->objects, *next; p != 0; p = next) { next = p->next(); + void* q = dynamic_cast(p); p->~instance_holder(); - instance_holder::deallocate(inst, dynamic_cast(p)); + instance_holder::deallocate(inst, q); } // Python 2.2.1 won't add weak references automatically when @@ -374,12 +376,7 @@ namespace objects // like, so we'll store the total size of the object // there. A negative number indicates that the extra // instance memory is not yet allocated to any holders. -#if PY_VERSION_HEX >= 0x02060000 - Py_SIZE(result) = -#else - result->ob_size = -#endif - -(static_cast(offsetof(instance<>,storage) + instance_size)); + Py_SET_SIZE(result,-static_cast(offsetof(instance<>,storage) + instance_size)); } return (PyObject*)result; } @@ -470,7 +467,7 @@ namespace objects { if (class_type_object.tp_dict == 0) { - Py_TYPE(&class_type_object) = incref(class_metatype().get()); + Py_SET_TYPE(&class_type_object, incref(class_metatype().get())); class_type_object.tp_base = &PyBaseObject_Type; if (PyType_Ready(&class_type_object)) return type_handle(); @@ -506,6 +503,16 @@ namespace objects ); } + object qualname(const char *name) + { +#if PY_VERSION_HEX >= 0x03030000 + if (PyObject_HasAttrString(scope().ptr(), "__qualname__")) { + return str("%s.%s" % make_tuple(scope().attr("__qualname__"), name)); + } +#endif + return str(name); + } + namespace { // Find a registered class object corresponding to id. Return a @@ -568,6 +575,9 @@ namespace objects object m = module_prefix(); if (m) d["__module__"] = m; +#if PY_VERSION_HEX >= 0x03030000 + d["__qualname__"] = qualname(name); +#endif if (doc != 0) d["__doc__"] = doc; @@ -618,7 +628,7 @@ namespace objects { object property( (python::detail::new_reference) - PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast("Osss"), fget.ptr(), 0, 0, docstr)); + PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast("Osss"), fget.ptr(), (char*)NULL, (char*)NULL, docstr)); this->setattr(name, property); } @@ -628,7 +638,7 @@ namespace objects { object property( (python::detail::new_reference) - PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast("OOss"), fget.ptr(), fset.ptr(), 0, docstr)); + PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast("OOss"), fget.ptr(), fset.ptr(), (char*)NULL, docstr)); this->setattr(name, property); } @@ -726,28 +736,46 @@ namespace objects } // namespace objects -void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std::size_t holder_size) +typedef unsigned int alignment_marker_t; + +void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std::size_t holder_size, std::size_t alignment) { assert(PyType_IsSubtype(Py_TYPE(Py_TYPE(self_)), &class_metatype_object)); objects::instance<>* self = (objects::instance<>*)self_; - int total_size_needed = holder_offset + holder_size; + int total_size_needed = holder_offset + holder_size + alignment - 1; if (-Py_SIZE(self) >= total_size_needed) { // holder_offset should at least point into the variable-sized part assert(holder_offset >= offsetof(objects::instance<>,storage)); + size_t allocated = holder_size + alignment; + void* storage = (char*)self + holder_offset; + void* aligned_storage = ::boost::alignment::align(alignment, holder_size, storage, allocated); + // Record the fact that the storage is occupied, noting where it starts - Py_SIZE(self) = holder_offset; - return (char*)self + holder_offset; + const size_t offset = reinterpret_cast(aligned_storage) - reinterpret_cast(storage) + holder_offset; + Py_SET_SIZE(self, offset); + return (char*)self + offset; } else { - void* const result = PyMem_Malloc(holder_size); - if (result == 0) + const size_t base_allocation = sizeof(alignment_marker_t) + holder_size + alignment - 1; + void* const base_storage = PyMem_Malloc(base_allocation); + if (base_storage == 0) throw std::bad_alloc(); - return result; + + const uintptr_t x = reinterpret_cast(base_storage) + sizeof(alignment_marker_t); + // Padding required to align the start of a data structure is: (alignment - (x % alignment)) % alignment + // Since the alignment is a power of two, the formula can be simplified with bitwise AND operator as follow: + const uintptr_t padding = (alignment - (x & (alignment - 1))) & (alignment - 1); + const size_t aligned_offset = sizeof(alignment_marker_t) + padding; + void* const aligned_storage = (char *)base_storage + aligned_offset; + BOOST_ASSERT((char *) aligned_storage + holder_size <= (char *)base_storage + base_allocation); + alignment_marker_t* const marker_storage = reinterpret_cast((char *)aligned_storage - sizeof(alignment_marker_t)); + *marker_storage = static_cast(padding); + return aligned_storage; } } @@ -757,7 +785,9 @@ void instance_holder::deallocate(PyObject* self_, void* storage) throw() objects::instance<>* self = (objects::instance<>*)self_; if (storage != (char*)self + Py_SIZE(self)) { - PyMem_Free(storage); + alignment_marker_t* marker_storage = reinterpret_cast((char *)storage - sizeof(alignment_marker_t)); + void *malloced_storage = (char *) storage - sizeof(alignment_marker_t) - (*marker_storage); + PyMem_Free(malloced_storage); } } diff --git a/src/object/enum.cpp b/src/object/enum.cpp index 10122ad1da..94df8e4aea 100644 --- a/src/object/enum.cpp +++ b/src/object/enum.cpp @@ -49,7 +49,9 @@ extern "C" if (!self->name) { return -#if PY_VERSION_HEX >= 0x03000000 +#if PY_VERSION_HEX >= 0x03030000 + PyUnicode_FromFormat("%S.%S(%ld)", mod, ((PyHeapTypeObject*)(self_->ob_type))->ht_qualname, PyLong_AsLong(self_)); +#elif PY_VERSION_HEX >= 0x03000000 PyUnicode_FromFormat("%S.%s(%ld)", mod, self_->ob_type->tp_name, PyLong_AsLong(self_)); #else PyString_FromFormat("%s.%s(%ld)", PyString_AsString(mod), self_->ob_type->tp_name, PyInt_AS_LONG(self_)); @@ -62,7 +64,9 @@ extern "C" return 0; return -#if PY_VERSION_HEX >= 0x03000000 +#if PY_VERSION_HEX >= 0x03030000 + PyUnicode_FromFormat("%S.%S.%S", mod, ((PyHeapTypeObject*)(self_->ob_type))->ht_qualname, name); +#elif PY_VERSION_HEX >= 0x03000000 PyUnicode_FromFormat("%S.%s.%S", mod, self_->ob_type->tp_name, name); #else PyString_FromFormat("%s.%s.%s", @@ -113,7 +117,6 @@ static PyTypeObject enum_type_object = { #if PY_VERSION_HEX < 0x03000000 | Py_TPFLAGS_CHECKTYPES #endif - | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ @@ -146,6 +149,7 @@ static PyTypeObject enum_type_object = { }; object module_prefix(); +object qualname(const char *name); namespace { @@ -153,7 +157,7 @@ namespace { if (enum_type_object.tp_dict == 0) { - Py_TYPE(&enum_type_object) = incref(&PyType_Type); + Py_SET_TYPE(&enum_type_object, incref(&PyType_Type)); #if PY_VERSION_HEX >= 0x03000000 enum_type_object.tp_base = &PyLong_Type; #else @@ -176,6 +180,11 @@ namespace object module_name = module_prefix(); if (module_name) d["__module__"] = module_name; +#if PY_VERSION_HEX >= 0x03030000 + object q = qualname(name); + if (q) + d["__qualname__"] = q; +#endif if (doc) d["__doc__"] = doc; diff --git a/src/object/function.cpp b/src/object/function.cpp index 5c59cc7798..fec56768da 100644 --- a/src/object/function.cpp +++ b/src/object/function.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -107,7 +107,7 @@ function::function( PyObject* p = this; if (Py_TYPE(&function_type) == 0) { - Py_TYPE(&function_type) = &PyType_Type; + Py_SET_TYPE(&function_type, &PyType_Type); ::PyType_Ready(&function_type); } @@ -158,15 +158,9 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const { // no argument preprocessing } - else if (n_actual > max_arity) - { - // too many arguments - inner_args = handle<>(); - } else { // build a new arg tuple, will adjust its size later - assert(max_arity <= static_cast(ssize_t_max)); inner_args = handle<>( PyTuple_New(static_cast(max_arity))); @@ -424,6 +418,30 @@ namespace detail extern char cpp_signature_tag[]; } +object const& function::add_doc(object const& attribute, char const* doc) +{ + str _doc; + + if (docstring_options::show_py_signatures_) + { + _doc += str(const_cast(detail::py_signature_tag)); + } + if (doc != 0 && docstring_options::show_user_defined_) + _doc += doc; + + if (docstring_options::show_cpp_signatures_) + { + _doc += str(const_cast(detail::cpp_signature_tag)); + } + if(_doc) + { + object mutable_attribute(attribute); + mutable_attribute.attr("__doc__")= _doc; + } + + return attribute; +} + void function::add_to_namespace( object const& name_space, char const* name_, object const& attribute, char const* doc) { @@ -449,7 +467,9 @@ void function::add_to_namespace( if (dict == 0) throw_error_already_set(); + assert(!PyErr_Occurred()); handle<> existing(allow_null(::PyObject_GetItem(dict.get(), name.ptr()))); + PyErr_Clear(); if (existing) { @@ -490,16 +510,28 @@ void function::add_to_namespace( if (new_func->name().is_none()) new_func->m_name = name; + assert(!PyErr_Occurred()); handle<> name_space_name( - allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast("__name__")))); + allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast( +#if PY_VERSION_HEX < 0x03030000 + "__name__" +#else + "__qualname__" +#endif + )))); + PyErr_Clear(); if (name_space_name) new_func->m_namespace = object(name_space_name); + + object module_name( + PyObject_IsInstance(name_space.ptr(), upcast(&PyModule_Type)) + ? object(name_space.attr("__name__")) + : api::getattr(name_space, "__module__", str()) + ); + new_func->m_module = module_name; } - // The PyObject_GetAttrString() or PyObject_GetItem calls above may - // have left an active error - PyErr_Clear(); if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0) throw_error_already_set(); @@ -536,24 +568,7 @@ void function::add_to_namespace( "C++ signature:", f->signature(true))); } */ - str _doc; - - if (docstring_options::show_py_signatures_) - { - _doc += str(const_cast(detail::py_signature_tag)); - } - if (doc != 0 && docstring_options::show_user_defined_) - _doc += doc; - - if (docstring_options::show_cpp_signatures_) - { - _doc += str(const_cast(detail::cpp_signature_tag)); - } - if(_doc) - { - object mutable_attribute(attribute); - mutable_attribute.attr("__doc__")= _doc; - } + add_doc(attribute, doc); } BOOST_PYTHON_DECL void add_to_namespace( @@ -568,6 +583,18 @@ BOOST_PYTHON_DECL void add_to_namespace( function::add_to_namespace(name_space, name, attribute, doc); } +BOOST_PYTHON_DECL object const& add_doc(object const& attribute, char const* doc) +{ +#if PY_VERSION_HEX >= 0x03000000 + if (PyInstanceMethod_Check(attribute.ptr())) { +#else + if (PyMethod_Check(attribute.ptr())) { +#endif + return attribute; + } + return function::add_doc(attribute, doc); +} + namespace { @@ -674,7 +701,7 @@ extern "C" static PyObject* function_get_module(PyObject* op, void*) { function* f = downcast(op); - object const& ns = f->get_namespace(); + object const& ns = f->get_module(); if (!ns.is_none()) { return python::incref(ns.ptr()); } diff --git a/src/object/function_doc_signature.cpp b/src/object/function_doc_signature.cpp index 41695285ac..76b620dcb9 100644 --- a/src/object/function_doc_signature.cpp +++ b/src/object/function_doc_signature.cpp @@ -114,23 +114,58 @@ namespace boost { namespace python { namespace objects { return res; } - const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s) + static str get_qualname(const PyTypeObject *py_type) + { +# if PY_VERSION_HEX >= 0x03030000 + if ( py_type->tp_flags & Py_TPFLAGS_HEAPTYPE ) + return str(handle<>(borrowed(((PyHeapTypeObject*)(py_type))->ht_qualname))); +# endif + return str(py_type->tp_name); + } + + str function_doc_signature_generator::py_type_str(const python::detail::signature_element &s, const object ¤t_module_name) { if (s.basename==std::string("void")){ static const char * none = "None"; - return none; + return str(none); } PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0; - if ( py_type ) - return py_type->tp_name; - else{ + if ( py_type ) { + 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; +#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( + type_module_name, + current_module_name.ptr(), + Py_NE + ) != 0 + ) { + 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 { static const char * object = "object"; - return object; + return str(object); } } - str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types) + str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, const object& current_module_name, bool cpp_types) { str param; @@ -156,12 +191,12 @@ namespace boost { namespace python { namespace objects { { object kv; if ( arg_names && (kv = arg_names[n-1]) ) - param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) ); + param = str( " (%s)%s" % make_tuple(py_type_str(s[n], current_module_name),kv[0]) ); else - param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) ); + param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n], current_module_name),"arg", n) ); } else //we are processing the return type - param = py_type_str(f.get_return_type()); + param = py_type_str(f.get_return_type(), current_module_name); } //an argument - check for default value and append it @@ -199,7 +234,7 @@ namespace boost { namespace python { namespace objects { str param; formal_params.append( - parameter_string(impl, n, f->m_arg_names, cpp_types) + parameter_string(impl, n, f->m_arg_names, f->get_module(), cpp_types) ); // find all the arguments with default values preceeding the arity-n_overloads diff --git a/src/object/inheritance.cpp b/src/object/inheritance.cpp index 7dc9db1cd7..44062875a4 100644 --- a/src/object/inheritance.cpp +++ b/src/object/inheritance.cpp @@ -4,6 +4,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include #include +#include #include #if _MSC_FULL_VER >= 13102171 && _MSC_FULL_VER <= 13102179 # include @@ -11,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -184,6 +185,7 @@ namespace // map a type to a position in the index inline type_index_t::iterator type_position(class_id type) { + using namespace boost::placeholders; typedef index_entry entry; return std::lower_bound( @@ -389,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) @@ -451,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; @@ -489,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(*demand_type(static_id)) = get_dynamic_id; } diff --git a/src/object/iterator.cpp b/src/object/iterator.cpp index 3f6c4adacd..6b885a982c 100644 --- a/src/object/iterator.cpp +++ b/src/object/iterator.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include namespace boost { namespace python { namespace objects { diff --git a/src/object/life_support.cpp b/src/object/life_support.cpp index b7e9aa861e..281c3bffc5 100644 --- a/src/object/life_support.cpp +++ b/src/object/life_support.cpp @@ -93,7 +93,7 @@ PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient) if (Py_TYPE(&life_support_type) == 0) { - Py_TYPE(&life_support_type) = &PyType_Type; + Py_SET_TYPE(&life_support_type, &PyType_Type); PyType_Ready(&life_support_type); } diff --git a/src/slice.cpp b/src/slice.cpp index ee55f94846..5ff56185de 100644 --- a/src/slice.cpp +++ b/src/slice.cpp @@ -34,4 +34,14 @@ slice_base::step() const ((PySliceObject*)this->ptr())->step)); } +static struct register_slice_pytype_ptr +{ + register_slice_pytype_ptr() + { + const_cast( + converter::registry::lookup(boost::python::type_id()) + ).m_class_object = &PySlice_Type; + } +}register_slice_pytype_ptr_; + } } } // !namespace boost::python::detail diff --git a/src/wrapper.cpp b/src/wrapper.cpp index f8feaef947..2b053d8311 100644 --- a/src/wrapper.cpp +++ b/src/wrapper.cpp @@ -21,20 +21,28 @@ namespace detail this->m_self, const_cast(name)))) ) { - PyObject* borrowed_f = 0; - + PyObject* class_f = 0; + if ( PyMethod_Check(m.get()) - && ((PyMethodObject*)m.get())->im_self == this->m_self + && 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(name), &class_f) < 0) { + throw_error_already_set(); + } +#else + class_f = ::PyDict_GetItemString( class_object->tp_dict, const_cast(name)); - - + Py_XINCREF(class_f); +#endif } - if (borrowed_f != ((PyMethodObject*)m.get())->im_func) + bool is_override = (class_f != PyMethod_GET_FUNCTION(m.get())); + Py_XDECREF(class_f); + if (is_override) return override(m); } } diff --git a/test/Jamfile b/test/Jamfile index 7f088cf758..40115d86cb 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -2,14 +2,16 @@ # Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +require-b2 5.0.1 ; +import-search /boost/config/checks ; + import python ; import os ; -import ../../config/checks/config : requires ; +import config : requires ; lib socket ; -use-project /boost/python : ../build ; -project /boost/python/test +project : requirements gcc:-Wextra qnxnto:socket @@ -28,7 +30,7 @@ rule py-run ( sources * : input-file ? ) : $(input-file) : #requirements BOOST_PYTHON_SUPPRESS_REGISTRY_INITIALIZATION - + ] ; } @@ -50,6 +52,20 @@ rule require-windows ( properties * ) } } +if [ python.configured ] +{ +alias base_deps : usage-requirements + /boost/align//boost_align + /boost/assert//boost_assert + /boost/config//boost_config + /boost/core//boost_core + /boost/detail//boost_detail + /boost/function//boost_function + /boost/mpl//boost_mpl + /boost/preprocessor//boost_preprocessor + /boost/static_assert//boost_static_assert + /boost/type_traits//boost_type_traits + ; test-suite python : @@ -95,14 +111,18 @@ bpl-test crossmod_exception [ bpl-test andreas_beyer ] [ bpl-test wrapper_held_type ] -[ bpl-test polymorphism2_auto_ptr - : polymorphism2_auto_ptr.py polymorphism2.py polymorphism2_auto_ptr.cpp +[ bpl-test polymorphism2_auto_ptr + : polymorphism2_auto_ptr.py polymorphism2.py polymorphism2_auto_ptr.cpp + : [ requires auto_ptr ] ] [ bpl-test polymorphism ] [ bpl-test polymorphism2 ] -[ bpl-test auto_ptr ] +[ bpl-test auto_ptr + : # files + : [ requires auto_ptr ] +] [ bpl-test minimal ] [ bpl-test args ] @@ -113,7 +133,7 @@ bpl-test crossmod_exception [ bpl-test try : newtest.py m1.cpp m2.cpp ] [ bpl-test const_argument ] [ bpl-test keywords : keywords.cpp keywords_test.py ] - + [ python-extension builtin_converters_ext : builtin_converters.cpp /boost/python//boost_python ] [ bpl-test builtin_converters : test_builtin_converters.py builtin_converters_ext ] @@ -126,6 +146,7 @@ bpl-test crossmod_exception [ bpl-test object ] [ bpl-test class ] +[ bpl-test aligned_class ] [ bpl-test list ] [ bpl-test long ] [ bpl-test dict ] @@ -185,13 +206,13 @@ bpl-test crossmod_opaque # Whenever the cause for the failure of the polymorphism test is found # and fixed, this should be retested. hp_cxx:no ] - + [ python-extension map_indexing_suite_ext : map_indexing_suite.cpp int_map_indexing_suite.cpp a_map_indexing_suite.cpp /boost/python//boost_python ] -[ bpl-test +[ bpl-test map_indexing_suite : map_indexing_suite.py map_indexing_suite_ext ] - + [ run import_.cpp /boost/python//boost_python $(PY) : : import_.py ] # if $(TEST_BIENSTMAN_NON_BUGS) @@ -205,28 +226,29 @@ bpl-test crossmod_opaque # --- unit tests of library components --- -[ compile indirect_traits_test.cpp ] -[ run destroy_test.cpp ] +[ compile indirect_traits_test.cpp : base_deps ] +[ run destroy_test.cpp : : : base_deps ] [ py-run pointer_type_id_test.cpp ] [ py-run bases.cpp ] -[ run if_else.cpp ] +[ run if_else.cpp : : : base_deps ] [ py-run pointee.cpp ] -[ run result.cpp ] +[ run result.cpp : : : base_deps ] -[ compile string_literal.cpp ] +[ compile string_literal.cpp : base_deps ] [ py-compile borrowed.cpp ] [ py-compile object_manager.cpp ] [ py-compile copy_ctor_mutates_rhs.cpp ] [ py-run upcast.cpp ] - + [ py-compile select_holder.cpp ] - -[ run select_from_python_test.cpp ../src/converter/type_id.cpp - : + +[ run select_from_python_test.cpp ../src/converter/type_id.cpp + : : : BOOST_PYTHON_STATIC_LIB $(PY) + base_deps ] @@ -248,3 +270,4 @@ bpl-test crossmod_opaque ; +} diff --git a/test/SConscript b/test/SConscript deleted file mode 100644 index eaeefaca89..0000000000 --- a/test/SConscript +++ /dev/null @@ -1,169 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2016 Stefan Seefeld -# All rights reserved. -# -# 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) - -import platform -import sys - -Import('env') - -if sys.platform == 'win32': - # HACK: This works around a bug in SCons. - # subprocess.check_output will complain unless all environment - # variables are strings. - system_root = env['ENV']['SystemRoot'] - env['ENV']['SystemRoot'] = str(system_root) - - -# libs needed for embedding -ELIBS=env['LIBS'] + env['PYTHONLIBS'] - -def BPLTest(env, name, sources = None, deps = None): - run = env.BoostRunPythonScript(name + '.py') - if sources: - for source in sources: - Depends(run, - env.PythonExtension(source != name and source or (source + '_ext'), source + '.cpp') - ) - else: - Depends(run, env.PythonExtension(name + '_ext', name + '.cpp')) - if deps: - Depends(run, deps) - return run - -env.AddMethod(BPLTest) - -env.AppendENVPath('PYTHONPATH', Dir('.').path) - -tests=[] -tests+=env.BPLTest('crossmod_exception', ['crossmod_exception_a', 'crossmod_exception_b']) - -for test in [('injected',), - ('properties',), - ('return_arg',), - ('staticmethod',), - ('boost_shared_ptr',), - ('enable_shared_from_this',), - ('andreas_beyer',), - ('polymorphism',), - ('polymorphism2',), - ('wrapper_held_type',), - ('minimal',), - ('args',), - ('raw_ctor',), - ('exception_translator',), - ('test_enum', ['enum_ext']), - ('test_cltree', ['cltree']), - ('newtest', ['m1', 'm2']), - ('const_argument',), - ('keywords_test', ['keywords']), - ('test_pointer_adoption',), - ('operators',), - ('operators_wrapper',), - ('callbacks',), - ('defaults',), - ('object',), - ('list',), - ('long',), - ('dict',), - ('tuple',), - ('str',), - ('slice',), - ('virtual_functions',), - ('back_reference',), - ('implicit',), - ('data_members',), - ('ben_scott1',), - ('bienstman1',), - ('bienstman2',), - ('bienstman3',), - ('multi_arg_constructor',), - ('iterator', ['iterator', 'input_iterator']), - ('stl_iterator',), - ('extract',), - ('crossmod_opaque', ['crossmod_opaque_a', 'crossmod_opaque_b']), - ('opaque',), -# ('voidptr',), - ('pickle1',), - ('pickle2',), - ('pickle3',), - ('pickle4',), - ('nested',), - ('docstring',), - ('pytype_function',), - ('vector_indexing_suite',), - ('pointer_vector',)]: - tests+=env.BPLTest(*test) - -if env['CXX11']: - for test in [ - ('shared_ptr',), - ]: - tests+=env.BPLTest(*test) -else: - for test in [ - ('polymorphism2_auto_ptr',), - ('auto_ptr',), - ]: - tests+=env.BPLTest(*test) - - -test = env.BoostRunPythonScript('test_builtin_converters.py') -Depends( - test, - env.PythonExtension('builtin_converters_ext', ['builtin_converters.cpp']) - ) -tests+=test -test = env.BoostRunPythonScript('map_indexing_suite.py') -Depends( - test, - env.PythonExtension('map_indexing_suite_ext', [ - 'map_indexing_suite.cpp', - 'int_map_indexing_suite.cpp', - 'a_map_indexing_suite.cpp']) - ) -tests+=test - -tests+=env.BoostRunTest('import_', 'import_.cpp', '${SOURCES[0]} ${SOURCES[1]}', 'import_.py', LIBS=ELIBS) - -tests+=env.BoostCompileTest('indirect_traits_test') -tests+=env.BoostRunTests(['destroy_test', - 'pointer_type_id_test', - 'bases', - 'if_else', - 'pointee', - 'result'], LIBS=ELIBS) - -tests+=env.BoostCompileTests(['string_literal', - 'borrowed', - 'object_manager', - 'copy_ctor_mutates_rhs']) - -tests+=env.BoostRunTest('upcast', LIBS=ELIBS) -tests+=env.BoostCompileTest('select_holder') -tests+=env.BoostRunTest('select_from_python_test', LIBS=ELIBS) -tests+=env.BoostCompileTest('select_arg_to_python_test') - -if platform.system() == 'Windows': - tests+=env.BPLTest('calling_conventions') - tests+=env.BPLTest('calling_conventions_mf') - -if env['NUMPY']: - numpy_env = env.Clone() - numpy_env.BoostUseLib('numpy') - for test in [('numpy/dtype',), - ('numpy/ufunc',), - ('numpy/templates',), - ('numpy/ndarray',), - ('numpy/indexing',), - ('numpy/shapes',),]: - tests+=numpy_env.BPLTest(*test) - - -env.BoostTestSummary(tests) -AlwaysBuild(tests) diff --git a/test/aligned_class.cpp b/test/aligned_class.cpp new file mode 100644 index 0000000000..55f0fa3c70 --- /dev/null +++ b/test/aligned_class.cpp @@ -0,0 +1,33 @@ +// 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) +#include +#include +#include +#include +#include +#include + +using namespace boost::python; + +struct BOOST_ALIGNMENT(32) X +{ + int x; + BOOST_ALIGNMENT(32) float f; + X(int n, float _f) : x(n), f(_f) + { + BOOST_ASSERT((reinterpret_cast(&f) % 32) == 0); + } +}; + +int x_function(X& x) { return x.x;} +float f_function(X& x) { return x.f;} + +BOOST_PYTHON_MODULE(aligned_class_ext) +{ + class_("X", init()); + def("x_function", x_function); + def("f_function", f_function); +} + +#include "module_tail.cpp" diff --git a/test/aligned_class.py b/test/aligned_class.py new file mode 100755 index 0000000000..eb27ac1e96 --- /dev/null +++ b/test/aligned_class.py @@ -0,0 +1,44 @@ +# 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) +''' +>>> from aligned_class_ext import * + +Ensure sanity: + + >>> x = X(42, 16) + >>> x_function(x) + 42 + >>> f_function(x) + 16.0 + +Demonstrate extraction in the presence of metaclass changes: + + >>> class MetaX(X.__class__): + ... def __new__(cls, *args): + ... return super(MetaX, cls).__new__(cls, *args) + >>> class XPlusMetatype(X): + ... __metaclass__ = MetaX + >>> x = XPlusMetatype(42, 16) + >>> x_function(x) + 42 + >>> f_function(x) + 16.0 + + +''' + +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) diff --git a/test/args.py b/test/args.py index e884c06bea..5d89921467 100644 --- a/test/args.py +++ b/test/args.py @@ -1,15 +1,19 @@ # 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) -from __future__ import print_function """ >>> from args_ext import * ->>> raw(3, 4, foo = 'bar', baz = 42) -((3, 4), {'foo': 'bar', 'baz': 42}) +>>> args, kwargs = raw(3, 4, foo = 'bar', baz = 42) +>>> args +(3, 4) +>>> kwargs['foo'] +'bar' +>>> kwargs['baz'] +42 Prove that we can handle empty keywords and non-keywords - + >>> raw(3, 4) ((3, 4), {}) @@ -76,7 +80,7 @@ ... else: print('expected an exception: unknown keyword') Exercise member functions using default stubs - + >>> q.f1(z = 'nix', y = .125, x = 2) (2, 0.125, 'nix') >>> q.f1(y = .125, x = 2) @@ -123,10 +127,16 @@ 1 >>> y = Y(value = 33) ->>> y.raw(this = 1, that = 'the other')[1] -{'this': 1, 'that': 'the other'} +>>> _, kwargs = y.raw(this = 1, that = 'the other') +>>> kwargs['this'] +1 +>>> kwargs['that'] +'the other' """ + +from __future__ import print_function + def run(args = None): import sys import doctest @@ -143,6 +153,3 @@ def run(args = None): import args_ext help(args_ext) sys.exit(status) - - - diff --git a/test/back_reference.cpp b/test/back_reference.cpp index 266ed29125..11e47b3321 100644 --- a/test/back_reference.cpp +++ b/test/back_reference.cpp @@ -99,7 +99,7 @@ BOOST_PYTHON_MODULE(back_reference_ext) .def("set", &Y::set) ; - class_ >("Z", init()) + class_ >("Z", init()) .def("value", &Z::value) .def("set", &Z::set) ; diff --git a/test/calling_conventions.cpp b/test/calling_conventions.cpp index 54f49aebc3..c1c2b5a411 100644 --- a/test/calling_conventions.cpp +++ b/test/calling_conventions.cpp @@ -17,9 +17,11 @@ //------------------------------------------------------------------------------ // this section is the main body of the test extension module -#define BOOST_PYTHON_ENABLE_CDECL -#define BOOST_PYTHON_ENABLE_STDCALL -#define BOOST_PYTHON_ENABLE_FASTCALL +#if defined(_WIN32) && !defined(_WIN64) +# define BOOST_PYTHON_ENABLE_CDECL +# define BOOST_PYTHON_ENABLE_STDCALL +# define BOOST_PYTHON_ENABLE_FASTCALL +#endif #include #include #include diff --git a/test/calling_conventions_mf.cpp b/test/calling_conventions_mf.cpp index 80ccc4068b..83a97acfef 100644 --- a/test/calling_conventions_mf.cpp +++ b/test/calling_conventions_mf.cpp @@ -17,9 +17,11 @@ //------------------------------------------------------------------------------ // this section is the main body of the test extension module -#define BOOST_PYTHON_ENABLE_CDECL -#define BOOST_PYTHON_ENABLE_STDCALL -#define BOOST_PYTHON_ENABLE_FASTCALL +#if defined(_WIN32) && !defined(_WIN64) +# define BOOST_PYTHON_ENABLE_CDECL +# define BOOST_PYTHON_ENABLE_STDCALL +# define BOOST_PYTHON_ENABLE_FASTCALL +#endif #include #include #include diff --git a/test/copy_ctor_mutates_rhs.cpp b/test/copy_ctor_mutates_rhs.cpp index 41eac495e4..be52c4f327 100644 --- a/test/copy_ctor_mutates_rhs.cpp +++ b/test/copy_ctor_mutates_rhs.cpp @@ -9,14 +9,13 @@ struct foo { - operator std::auto_ptr&() const; + operator std::shared_ptr&() const; }; int main() { using namespace boost::python::detail; BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs::value); - BOOST_STATIC_ASSERT(copy_ctor_mutates_rhs >::value); BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs::value); BOOST_STATIC_ASSERT(!copy_ctor_mutates_rhs::value); return 0; diff --git a/test/dict.cpp b/test/dict.cpp index 375905d69a..4f3490a421 100644 --- a/test/dict.cpp +++ b/test/dict.cpp @@ -21,11 +21,13 @@ object new_dict() object data_dict() { dict tmp1; - tmp1["key1"] = "value1"; dict tmp2; tmp2["key2"] = "value2"; tmp1[1] = tmp2; + + tmp1["key1"] = "value1"; + return tmp1; } @@ -60,22 +62,20 @@ void work_with_dict(dict data1, dict data2) void test_templates(object print) { std::string key = "key"; - + dict tmp; - tmp[1] = "a test string"; - print(tmp.get(1)); - //print(tmp[1]); tmp[1.5] = 13; print(tmp.get(1.5)); + tmp[1] = "a test string"; + print(tmp.get(1)); print(tmp.get(44)); print(tmp); print(tmp.get(2,"default")); print(tmp.setdefault(3,"default")); BOOST_ASSERT(!tmp.has_key(key)); - //print(tmp[3]); } - + BOOST_PYTHON_MODULE(dict_ext) { def("new_dict", new_dict); diff --git a/test/dict.py b/test/dict.py index 72c37d99e1..9b1149ae60 100644 --- a/test/dict.py +++ b/test/dict.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function """ >>> from dict_ext import * >>> def printer(*args): @@ -22,14 +21,16 @@ >>> print(dict_from_sequence([(1,1),(2,2),(3,3)])) {1: 1, 2: 2, 3: 3} >>> test_templates(printer) #doctest: +NORMALIZE_WHITESPACE -a test string 13 +a test string None {1.5: 13, 1: 'a test string'} default default """ +from __future__ import print_function + def run(args = None): import sys import doctest @@ -37,7 +38,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/fabscript b/test/fabscript index 1989cc0d13..7cf22f9c09 100644 --- a/test/fabscript +++ b/test/fabscript @@ -21,14 +21,14 @@ src = module('..src') python_libs=python.instance().libs features |= runpath(src.bpl.path, base='') -def extension_test(name, ext=[], script=None, np=False, +def extension_test(name, exts=[], script=None, numpy=False, features=features, condition=None): """Create a Python extension test `name`. Arguments: * name: the name of the test. - * ext: extensions to be compiled, if none are given. + * exts: extensions to be compiled, if none are given. * script: the test script to execute, .py if none is given. - * np: if true, add boost_numpy to sources + * numpy: if true, add boost_numpy to sources * features: pre-defined features * condition: any condition under which to run the test Return: @@ -36,17 +36,17 @@ def extension_test(name, ext=[], script=None, np=False, features=features.copy() extensions = [] - libs = [src.bnl, src.bpl] if np else [src.bpl] - for e in ext or [name]: - if type(e) is str: # build from a single source file - n = e if e != name else e + '_ext' - s = [e + '.cpp'] + libs = [src.bnl, src.bpl] if numpy else [src.bpl] + for ext in exts or [name]: + if type(ext) is str: # build from a single source file + ext_name = ext if ext != name else ext + '_ext' + sources = [ext + '.cpp'] else: # build from a list of source files - n = e[0] if e[0] != name else e[0] + '_ext' - s = [n + '.cpp' for n in e] - e = extension(n, s + libs, features=features) - features |= pythonpath(e.path, base='') - extensions.append(e) + ext_name = ext[0] if ext[0] != name else ext[0] + '_ext' + sources = [source + '.cpp' for source in ext] + ext = extension(ext_name, sources + libs, features=features) + features |= pythonpath(ext.path, base='') + extensions.append(ext) if not script: script = name+'.py' return test(name, script, run=python.run, dependencies=extensions, @@ -67,6 +67,8 @@ for t in [('injected',), ('args',), ('raw_ctor',), ('exception_translator',), + ('module_init_exception',), + ('module_nogil',), ('test_enum', ['enum_ext']), ('test_cltree', ['cltree']), ('newtest', ['m1', 'm2']), @@ -78,6 +80,8 @@ for t in [('injected',), ('callbacks',), ('defaults',), ('object',), + ('class',), + ('aligned_class',), ('list',), ('long',), ('dict',), @@ -115,17 +119,17 @@ 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': command = """set PATH=$(runpath);%PATH% -$(>[1]) $(>[2])""" +$(>[0]) $(>[1])""" else: - command = 'LD_LIBRARY_PATH=$(runpath) $(>[1]) $(>[2])' + command = 'LD_LIBRARY_PATH=$(runpath) $(>[0]) $(>[1])' tests.append(test('import', [import_, 'import_.py'], run=action('run', command), @@ -167,7 +171,7 @@ for t in ['numpy/dtype', 'numpy/ndarray', 'numpy/indexing', 'numpy/shapes']: - tests.append(extension_test(t, np=True, + tests.append(extension_test(t, numpy=True, condition=set.define.contains('HAS_NUMPY'))) default = report('report', tests, fail_on_failures=True) diff --git a/test/import_.cpp b/test/import_.cpp index 3e21de0bad..8e03d9b448 100644 --- a/test/import_.cpp +++ b/test/import_.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/test/injected.cpp b/test/injected.cpp index 73e1e14baa..82db3e82e6 100644 --- a/test/injected.cpp +++ b/test/injected.cpp @@ -17,7 +17,7 @@ typedef test_class<> X; X* empty() { return new X(1000); } -std::auto_ptr sum(int a, int b) { return std::auto_ptr(new X(a+b)); } +std::shared_ptr sum(int a, int b) { return std::shared_ptr(new X(a+b)); } boost::shared_ptr product(int a, int b, int c) { diff --git a/test/iterator.py b/test/iterator.py index 314a356767..0d75100a09 100644 --- a/test/iterator.py +++ b/test/iterator.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function ''' >>> from iterator_ext import * >>> from input_iterator import * @@ -25,7 +24,7 @@ Range2 wraps a transform_iterator which doubles the elements it traverses. This proves we can wrap input iterators - + >>> z2 = range2(x) >>> for y in z2: ... print(y) @@ -56,12 +55,15 @@ >>> ll.push_back(x) >>> for a in ll: #doctest: +NORMALIZE_WHITESPACE ... for b in a: -... print(b, end='') +... print(b, end=' ') ... print('') ... 1 3 5 1 3 5 7 ''' + +from __future__ import print_function + def run(args = None): import sys import doctest @@ -69,7 +71,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/list.py b/test/list.py index 913032db8a..f6cf87096f 100644 --- a/test/list.py +++ b/test/list.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function ''' >>> from list_ext import * @@ -41,7 +40,7 @@ ['h', 'e', 'l', 'l', 'o', '.'] tuples do not automatically convert to lists when passed as arguments - + >>> try: append_list(letters, (1,2)) ... except TypeError: pass ... else: print('expected an exception') @@ -51,7 +50,7 @@ ['h', 'e', 'l', 'l', 'o', '.', [1, 2]] Check that subclass functions are properly called - + >>> class mylist(list): ... def append(self, o): ... list.append(self, o) @@ -103,6 +102,8 @@ ['y', 'x', 'o', 'l', 'l', 'h', 'e', '.'] ''' +from __future__ import print_function + def run(args = None): import sys import doctest @@ -110,7 +111,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/long.py b/test/long.py index f63923632e..157dc57aa9 100644 --- a/test/long.py +++ b/test/long.py @@ -1,9 +1,6 @@ # 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) -import sys -if (sys.version_info.major >= 3): - long = int ''' >>> from long_ext import * >>> print(new_long()) @@ -20,6 +17,10 @@ >>> x = Y(long(4294967295)) ''' +import sys +if (sys.version_info.major >= 3): + long = int + def run(args = None): import sys import doctest diff --git a/test/map_indexing_suite.py b/test/map_indexing_suite.py index a5750a833f..6d3e57a102 100644 --- a/test/map_indexing_suite.py +++ b/test/map_indexing_suite.py @@ -1,7 +1,6 @@ # Copyright Joel de Guzman 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) -from __future__ import print_function ''' ##################################################################### @@ -25,7 +24,7 @@ # test that a string is implicitly convertible # to an X ->>> x_value('bochi bochi') +>>> x_value('bochi bochi') 'gotya bochi bochi' ##################################################################### @@ -33,9 +32,9 @@ ##################################################################### >>> def print_xmap(xmap): ... s = '[ ' -... for x in xmap: +... for x in xmap: ... s += repr(x) -... s += ' ' +... s += ' ' ... s += ']' ... print(s) @@ -135,7 +134,7 @@ >>> assert not 12345 in xm ##################################################################### -# Some references to the container elements +# Some references to the container elements ##################################################################### >>> z0 = xm['joel'] @@ -156,7 +155,7 @@ kiwi ##################################################################### -# Delete some container element +# Delete some container element ##################################################################### >>> del xm['tenji'] @@ -168,7 +167,7 @@ [ (joel, apple) (kim, kiwi) (mariel, grape) ] ##################################################################### -# Show that the references are still valid +# Show that the references are still valid ##################################################################### >>> z0 # proxy apple @@ -199,7 +198,7 @@ >>> print_xmap(tm) [ (joel, aaa) (kimpo, bbb) ] >>> for el in tm: #doctest: +NORMALIZE_WHITESPACE -... print(el.key(), end='') +... print(el.key(), end=' ') ... dom = el.data() joel kimpo @@ -216,11 +215,19 @@ 4 ##################################################################### -# END.... +# Test signature... +##################################################################### + +>>> AMap.__iter__.__doc__.strip().split("\\n")[0] +'__iter__( (AMap)arg1) -> __main__.iterator :' + +##################################################################### +# END.... ##################################################################### ''' +from __future__ import print_function def run(args = None): import sys @@ -236,8 +243,3 @@ def run(args = None): status = run()[0] if (status == 0): print("Done.") sys.exit(status) - - - - - diff --git a/test/module_init_exception.cpp b/test/module_init_exception.cpp new file mode 100644 index 0000000000..d8cec57d3a --- /dev/null +++ b/test/module_init_exception.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2003 Rational Discovery LLC +// 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) + +#include +#include + +using namespace boost::python; + +BOOST_PYTHON_MODULE(module_init_exception_ext) +{ + throw std::runtime_error("Module init failed"); +} diff --git a/test/module_init_exception.py b/test/module_init_exception.py new file mode 100644 index 0000000000..3da53e1956 --- /dev/null +++ b/test/module_init_exception.py @@ -0,0 +1,12 @@ +# Copyright (C) 2003 Rational Discovery LLC. 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) + +print("running...") + +try: + import module_init_exception_ext +except RuntimeError as e: + print(e) + +print("Done.") diff --git a/test/module_nogil.cpp b/test/module_nogil.cpp new file mode 100644 index 0000000000..331a73cf31 --- /dev/null +++ b/test/module_nogil.cpp @@ -0,0 +1,25 @@ +// Test for BOOST_PYTHON_MODULE with optional mod_gil_not_used argument + +#include +#include + +// 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 diff --git a/test/module_nogil.py b/test/module_nogil.py new file mode 100644 index 0000000000..c035436014 --- /dev/null +++ b/test/module_nogil.py @@ -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) diff --git a/test/nested.cpp b/test/nested.cpp index de656d2b8b..3a0d05e5ea 100644 --- a/test/nested.cpp +++ b/test/nested.cpp @@ -4,6 +4,8 @@ // http://www.boost.org/LICENSE_1_0.txt) #include #include +#include +#include #include #include #include "test_class.hpp" @@ -16,6 +18,8 @@ typedef test_class<> X; typedef test_class<1> Y; +enum color { red = 0, blue = 1, green = 2 }; + std::ostream& operator<<(std::ostream& s, X const& x) { return s << x.value(); @@ -26,11 +30,13 @@ std::ostream& operator<<(std::ostream& s, Y const& x) return s << x.value(); } +void test_function(const X& x, const Y& y) {} BOOST_PYTHON_MODULE(nested_ext) { using namespace boost::python; + { // Establish X as the current scope. scope x_class = class_("X", init()) @@ -42,6 +48,17 @@ BOOST_PYTHON_MODULE(nested_ext) class_("Y", init()) .def(str(self)) ; + + // so will the enum `color` + enum_("color") + .value("red", red) + .value("green", green) + .value("blue", blue) + ; + } + + // The generated docstring will use the fully-qualified name of Y + def("test_function", &test_function); } diff --git a/test/nested.py b/test/nested.py index 720790173c..657d100a71 100644 --- a/test/nested.py +++ b/test/nested.py @@ -13,14 +13,35 @@ >>> X.__name__ 'X' - >>> X.Y + >>> X.Y # doctest: +py2 + + >>> X.Y # doctest: +py3 + >>> X.Y.__module__ 'nested_ext' >>> X.Y.__name__ 'Y' + + >>> getattr(X.color, "__qualname__", None) # doctest: +py3 + 'X.color' + + >>> repr(X.color.red) # doctest: +py2 + 'nested_ext.color.red' + + >>> repr(X.color.red) # doctest: +py3 + 'nested_ext.X.color.red' + + >>> repr(X.color(1)) # doctest: +py2 + 'nested_ext.color(1)' + + >>> repr(X.color(1)) # doctest: +py3 + 'nested_ext.X.color(1)' + + >>> test_function.__doc__.strip().split('\\n')[0] # doctest: +py3 + 'test_function( (X)arg1, (X.Y)arg2) -> None :' ''' @@ -30,7 +51,23 @@ def run(args = None): if args is not None: sys.argv = args - return doctest.testmod(sys.modules.get(__name__)) + + py2 = doctest.register_optionflag("py2") + py3 = doctest.register_optionflag("py3") + + class ConditionalChecker(doctest.OutputChecker): + def check_output(self, want, got, optionflags): + if (optionflags & py3) and (sys.version_info[0] < 3): + return True + if (optionflags & py2) and (sys.version_info[0] >= 3): + 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...") diff --git a/test/numpy/dtype.py b/test/numpy/dtype.py index a27ee0f55d..a2eabb58e2 100644 --- a/test/numpy/dtype.py +++ b/test/numpy/dtype.py @@ -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 @@ 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): diff --git a/test/numpy/indexing.py b/test/numpy/indexing.py index ebd9dcbabb..3fb9adb4c0 100644 --- a/test/numpy/indexing.py +++ b/test/numpy/indexing.py @@ -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. diff --git a/test/numpy/ndarray.py b/test/numpy/ndarray.py index 2acc384a52..13f3c73e42 100644 --- a/test/numpy/ndarray.py +++ b/test/numpy/ndarray.py @@ -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 @@ def testNdzeros(self): 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 @@ def testNdzeros_matrix(self): 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 @@ def testNdarray(self): 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) diff --git a/test/numpy/shapes.py b/test/numpy/shapes.py index d0a0099ca6..28c74b7b18 100644 --- a/test/numpy/shapes.py +++ b/test/numpy/shapes.py @@ -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. diff --git a/test/numpy/templates.py b/test/numpy/templates.py index 8290b13a07..9c21622881 100755 --- a/test/numpy/templates.py +++ b/test/numpy/templates.py @@ -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 @@ def testTemplates(self): 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) diff --git a/test/numpy/ufunc.py b/test/numpy/ufunc.py index e820121ee1..1fa3090b3e 100755 --- a/test/numpy/ufunc.py +++ b/test/numpy/ufunc.py @@ -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 @@ def testArray(self): 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 @@ def testArray(self): 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) diff --git a/test/object.py b/test/object.py index 67a46d9362..cf6c2de0c5 100644 --- a/test/object.py +++ b/test/object.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function ''' >>> from object_ext import * @@ -130,14 +129,14 @@ 1 Slices - + >>> assert check_string_slice() Operators ->>> def print_args(*args, **kwds): +>>> def print_args(*args, **kwds): ... print(args, kwds) ->>> test_call(print_args, (0, 1, 2, 3), {'a':'A'}) +>>> test_call(print_args, (0, 1, 2, 3), {'a':'A'}) (0, 1, 2, 3) {'a': 'A'} @@ -149,7 +148,7 @@ Now make sure that object is actually managing reference counts - + >>> import weakref >>> class Z: pass ... @@ -164,6 +163,8 @@ death ''' +from __future__ import print_function + def run(args = None): import sys import doctest @@ -171,7 +172,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/operators_wrapper.cpp b/test/operators_wrapper.cpp index 12f30048d0..e62ead16f8 100644 --- a/test/operators_wrapper.cpp +++ b/test/operators_wrapper.cpp @@ -36,7 +36,7 @@ BOOST_PYTHON_MODULE( operators_wrapper_ext ) ; scope().attr("v") = vector(); - std::auto_ptr dp(new dvector); - register_ptr_to_python< std::auto_ptr >(); + std::shared_ptr dp(new dvector); + register_ptr_to_python< std::shared_ptr >(); scope().attr("d") = dp; } diff --git a/test/pickle1.py b/test/pickle1.py index b8f4efd9b0..0df59a4b3a 100644 --- a/test/pickle1.py +++ b/test/pickle1.py @@ -9,8 +9,10 @@ 1 >>> pickle1_ext.world.__name__ 'world' - >>> pickle1_ext.world('Hello').__reduce__() + >>> pickle1_ext.world('Hello').__reduce__() # doctest: +PY310 (, ('Hello',)) + >>> pickle1_ext.world('Hello').__reduce__() # doctest: +PY311 + (, ('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...") diff --git a/test/pickle2.py b/test/pickle2.py index f4788d3b89..4c11ef3a85 100644 --- a/test/pickle2.py +++ b/test/pickle2.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function r'''>>> import pickle2_ext >>> import pickle >>> pickle2_ext.world.__module__ @@ -35,6 +34,8 @@ Incomplete pickle support (__getstate_manages_dict__ not set) ''' +from __future__ import print_function + def run(args = None): import sys import doctest @@ -42,7 +43,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/pickle3.py b/test/pickle3.py index 932e30f3cd..391e3d00f1 100644 --- a/test/pickle3.py +++ b/test/pickle3.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function r'''>>> import pickle3_ext >>> import pickle >>> pickle3_ext.world.__module__ @@ -30,6 +29,8 @@ Hello from California! 0 84 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 126.0 ''' +from __future__ import print_function + def run(args = None): import sys import doctest @@ -37,7 +38,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/pickle4.py b/test/pickle4.py index be813bbb13..3cf4d7241f 100644 --- a/test/pickle4.py +++ b/test/pickle4.py @@ -12,8 +12,10 @@ 1 >>> pickle4_ext.world.__name__ 'world' - >>> pickle4_ext.world('Hello').__reduce__() + >>> pickle4_ext.world('Hello').__reduce__() # doctest: +PY310 (, ('Hello',)) + >>> pickle4_ext.world('Hello').__reduce__() # doctest: +PY311 + (, ('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...") diff --git a/test/properties.cpp b/test/properties.cpp index d338beb915..aa1b0a05cf 100644 --- a/test/properties.cpp +++ b/test/properties.cpp @@ -64,6 +64,7 @@ BOOST_PYTHON_MODULE(properties_ext) class_("X", init() ) //defining read only property .add_property( "value_r", &X::get_value ) + .add_property( "value_r_f", make_function(&X::get_value) ) .add_property( "value_r_ds", &X::get_value, "value_r_ds is read-only") //defining read \ write property .add_property( "value_rw", &X::get_value, &X::set_value ) diff --git a/test/properties.py b/test/properties.py index 1bc7a624ba..e95d59bef2 100644 --- a/test/properties.py +++ b/test/properties.py @@ -20,6 +20,9 @@ >>> x1.value_r 1 +>>> x1.value_r_f +1 + value read - write >>> x1.value_rw 1 @@ -53,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 @@ -84,8 +86,27 @@ class instance count from object: >>> assert properties.X.value_rw_ds.__doc__ == "value_rw_ds is read-write" +>>> properties.X.value_r_f.fget.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1) -> int :' + +>>> properties.X.value_rw_ds.fget.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1) -> int :' + +>>> properties.X.value_rw_ds.fset.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1, (int)arg2) -> None :' + +>>> properties.X.value_rw_ds.fget.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1) -> int :' + +>>> properties.X.value_direct.fset.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1, (int)arg2) -> None :' + +>>> properties.X.value_direct.fget.__doc__.strip().split("\\n")[0] +'None( (properties_ext.X)arg1) -> int :' """ +# FIXME: cases to cover: pointer-to-member, preconstructed function + #import sys; sys.path.append(r'P:\Actimize4.0\smart_const\py_smart_const___Win32_Debug') import properties_ext as properties diff --git a/test/select_holder.cpp b/test/select_holder.cpp index 8650bd06a0..77aac67868 100644 --- a/test/select_holder.cpp +++ b/test/select_holder.cpp @@ -62,14 +62,14 @@ int test_main(int, char * []) assert_holder >(); - assert_holder - ,pointer_holder,Base> >(); + assert_holder + ,pointer_holder,Base> >(); - assert_holder - ,pointer_holder_back_reference,Base> >(); + assert_holder + ,pointer_holder_back_reference,Base> >(); - assert_holder - ,pointer_holder_back_reference,BR> > (); + assert_holder + ,pointer_holder_back_reference,BR> > (); return 0; } diff --git a/test/shared_ptr.py b/test/shared_ptr.py index d250ae7eca..4ef88f78d8 100644 --- a/test/shared_ptr.py +++ b/test/shared_ptr.py @@ -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 @@ 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 @@ 17 >>> try: modify(x) ... except TypeError: pass -... else: 'print(expected a TypeError)' +... else: print('expected a TypeError') >>> look(None) -1 >>> store(x) diff --git a/test/slice.py b/test/slice.py index c58bbc0299..041934cf57 100644 --- a/test/slice.py +++ b/test/slice.py @@ -33,6 +33,8 @@ 0 >>> check_slice_get_indices( slice( -2, -5, -2)) 6 +>>> check_slice_get_indices.__doc__.strip().split('\\n')[0] +'check_slice_get_indices( (slice)arg1) -> int :' """ # Performs an affirmative and negative argument resolution check. diff --git a/test/str.py b/test/str.py index 4eba0e8769..bfb4994959 100644 --- a/test/str.py +++ b/test/str.py @@ -1,11 +1,10 @@ # 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) -from __future__ import print_function """ >>> from str_ext import * >>> def printer(*args): -... for x in args: print(x, end='') +... for x in args: print(x, end=' ') ... print('') ... >>> work_with_string(printer) #doctest: +NORMALIZE_WHITESPACE @@ -38,6 +37,8 @@ aaaaaaaaaaaaaaaaaaaaa """ +from __future__ import print_function + def run(args = None): import sys import doctest @@ -45,7 +46,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/test_builtin_converters.py b/test/test_builtin_converters.py index e612ed23df..0f1b4ded75 100644 --- a/test/test_builtin_converters.py +++ b/test/test_builtin_converters.py @@ -1,9 +1,7 @@ +# -*- coding: utf-8 -*- # 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) -import sys -if (sys.version_info.major >= 3): - long = int r""" >>> from builtin_converters_ext import * @@ -17,7 +15,7 @@ # Wrappers to simplify tests >>> def should_pass(method, values): -... result = map(method, values[0]) +... result = list(map(method, values[0])) ... if result != values[0]: ... print("Got %s but expected %s" % (result, values[0])) >>> def test_overflow(method, values): @@ -136,9 +134,6 @@ >>> print(rewrap_value_wstring(u'yo, wassup?')) yo, wassup? ->>> print(rewrap_value_wstring(u'\U0001f4a9')) -\U0001f4a9 - test that overloading on unicode works: >>> print(rewrap_value_string(u'yo, wassup?')) diff --git a/test/test_class.hpp b/test/test_class.hpp index 5404fdba27..a9324e9c47 100644 --- a/test/test_class.hpp +++ b/test/test_class.hpp @@ -4,17 +4,17 @@ // http://www.boost.org/LICENSE_1_0.txt) #ifndef TEST_CLASS_DWA2002326_HPP # define TEST_CLASS_DWA2002326_HPP -# include +# include template struct test_class { explicit test_class(int x) : x(x), magic(7654321 + n) { ++counter; } test_class(test_class const& rhs) : x(rhs.x), magic(7654321 + n) { ++counter; } - virtual ~test_class() { BOOST_TEST(magic == 7654321 + n); magic = 6666666; x = 9999; --counter; } + virtual ~test_class() { BOOST_ASSERT(magic == 7654321 + n); magic = 6666666; x = 9999; --counter; } - void set(int _x) { BOOST_TEST(magic == 7654321 + n); this->x = _x; } - int value() const { BOOST_TEST(magic == 7654321 + n); return x; } + void set(int _x) { BOOST_ASSERT(magic == 7654321 + n); this->x = _x; } + int value() const { BOOST_ASSERT(magic == 7654321 + n); return x; } operator int() const { return x; } static int count() { return counter; } diff --git a/test/test_cltree.py b/test/test_cltree.py index 2127b7cdb6..d5df6fc5a0 100644 --- a/test/test_cltree.py +++ b/test/test_cltree.py @@ -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 diff --git a/test/tuple.py b/test/tuple.py index 1aec5fded4..e2cd5eb179 100644 --- a/test/tuple.py +++ b/test/tuple.py @@ -1,7 +1,6 @@ # 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) -from __future__ import print_function """ >>> from tuple_ext import * >>> def printer(*args): @@ -22,6 +21,8 @@ ('hello', 42) """ +from __future__ import print_function + def run(args = None): import sys import doctest @@ -29,7 +30,7 @@ def run(args = None): if args is not None: sys.argv = args return doctest.testmod(sys.modules.get(__name__)) - + if __name__ == '__main__': print("running...") import sys diff --git a/test/upcast.cpp b/test/upcast.cpp index 255429f168..e005900410 100644 --- a/test/upcast.cpp +++ b/test/upcast.cpp @@ -13,7 +13,7 @@ int main() { PyTypeObject o; Y y; - BOOST_TEST(&Py_REFCNT(boost::python::upcast(&o)) == &Py_REFCNT(&o)); - BOOST_TEST(&Py_REFCNT(boost::python::upcast(&y)) == &Py_REFCNT(&y)); + BOOST_TEST(boost::python::upcast(&o) == reinterpret_cast(&o)); + BOOST_TEST(boost::python::upcast(&y) == &y); return boost::report_errors(); } diff --git a/test/wrapper_held_type.cpp b/test/wrapper_held_type.cpp index e99422796e..ef494924b9 100644 --- a/test/wrapper_held_type.cpp +++ b/test/wrapper_held_type.cpp @@ -20,12 +20,12 @@ struct data } }; -std::auto_ptr create_data() +std::shared_ptr create_data() { - return std::auto_ptr( new data ); + return std::shared_ptr( new data ); } -void do_nothing( std::auto_ptr& ){} +void do_nothing( std::shared_ptr& ){} 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 );