././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/0000755000175100001660000000000014752665566012120 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4059956 cppy-1.3.1/.github/0000755000175100001660000000000014752665566013460 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4079957 cppy-1.3.1/.github/workflows/0000755000175100001660000000000014752665566015515 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/.github/workflows/ci.yml0000644000175100001660000000263614752665560016634 0ustar00runnerdockername: Continuous Integration on: push: branches: - main pull_request: paths: - .github/workflows/ci.yml - cppy/* - tests/* - setup.py - pyproject.toml jobs: lint: name: Lints Python codes runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: pyflakes uses: reviewdog/action-pyflakes@v1 with: github_token: ${{ secrets.github_token }} reporter: ${{ github.event_name == 'pull_request' && 'github-pr-review' || 'github-check' }} tests: name: Unit tests runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.10', '3.11', '3.12', '3.13'] fail-fast: false steps: - uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install pytest - name: Install project run: | pip install . - name: Test with pytest run: | pytest tests ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/.github/workflows/docs.yml0000644000175100001660000000203414752665560017161 0ustar00runnerdockername: Documentation building on: schedule: - cron: '0 0 * * 2' push: branches: - main pull_request: branches: - main paths: - .github/workflows/docs.yml - "cppy/**" - "docs/**" - setup.py - pyproject.toml jobs: docs: name: Docs building runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Set up Python uses: actions/setup-python@v5 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r docs/requirements.txt - name: Install project run: | pip install -e . - name: Install graphviz uses: ts-graphviz/setup-graphviz@v1 - name: Build documentation run: | mkdir docs_output; sphinx-build docs/source docs_output -W -b html; ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/.github/workflows/release.yml0000644000175100001660000000645614752665560017665 0ustar00runnerdockername: Build and upload release on: workflow_dispatch: schedule: - cron: '0 0 * * 3' push: tags: - '*' jobs: build_sdist: name: Build sdist runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Setup Python uses: actions/setup-python@v5 - name: Build sdist run: | pip install --upgrade pip pip install wheel build python -m build . -s - name: Test sdist run: | pip install pytest pip install dist/*.tar.gz cd .. python -m pytest cppy/tests - name: Store artifacts uses: actions/upload-artifact@v4 with: name: cibw-sdist path: dist/* build_universal_wheel: name: Build universal wheel runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: Setup Python uses: actions/setup-python@v5 - name: Build universal wheel run: | pip install --upgrade pip pip install wheel build python -m build . - name: Test wheel run: | pip install pytest pip install dist/*.whl cd .. python -m pytest cppy/tests - name: Store artifacts uses: actions/upload-artifact@v4 with: name: cibw-wheel path: dist/*.whl publish: if: github.event_name == 'push' needs: [build_universal_wheel, build_sdist] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4.1.7 with: pattern: cibw-* path: dist merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.pypi_password }} # To test: # repository_url: https://test.pypi.org/legacy/ github-release: name: >- Sign the Python 🐍 distribution 📦 with Sigstore and create a GitHub Release runs-on: ubuntu-latest needs: - publish permissions: contents: write id-token: write steps: - name: Download all the dists uses: actions/download-artifact@v4.1.7 with: pattern: cibw-* path: dist merge-multiple: true - name: Sign the dists with Sigstore uses: sigstore/gh-action-sigstore-python@v2.1.0 with: password: ${{ secrets.pypi_password }} inputs: >- ./dist/*.tar.gz ./dist/*.whl - name: Create GitHub Release env: GITHUB_TOKEN: ${{ github.token }} run: >- gh release create '${{ github.ref_name }}' --repo '${{ github.repository }}' --generate-notes - name: Upload artifact signatures to GitHub Release env: GITHUB_TOKEN: ${{ github.token }} run: >- gh release upload '${{ github.ref_name }}' dist/** --repo '${{ github.repository }}' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/.gitignore0000644000175100001660000000005714752665560014104 0ustar00runnerdockerbuild dist *.pyc *.egg-info .vscode version.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/.readthedocs.yaml0000644000175100001660000000111414752665560015336 0ustar00runnerdocker# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-20.04 tools: python: "3.9" # Build documentation in the docs/source directory with Sphinx sphinx: configuration: docs/source/conf.py # Enable epub output formats: - epub # Optionally declare the Python requirements required to build your docs python: install: - requirements: docs/requirements.txt - method: pip path: . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/CMakeLists.txt0000644000175100001660000000324214752665560014653 0ustar00runnerdockercmake_minimum_required(VERSION 3.12) # https://dominikberner.ch/cmake-interface-lib/ project( "cppy" VERSION 1.1.0 HOMEPAGE_URL "https://github.com/nucleic/cppy") set(libname "cppy") add_library(${libname} INTERFACE) # add alias so the project can be uses with add_subdirectory add_library(${libname}::${libname} ALIAS ${libname}) include(GNUInstallDirs) target_include_directories( ${libname} INTERFACE $ $) target_compile_features(${libname} INTERFACE cxx_std_11) install(TARGETS ${libname} EXPORT ${libname}_Targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${libname}ConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) configure_package_config_file( "${PROJECT_SOURCE_DIR}/cmake/${libname}Config.cmake.in" "${PROJECT_BINARY_DIR}/${libname}Config.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${libname}/cmake) install(EXPORT ${libname}_Targets FILE ${libname}Targets.cmake NAMESPACE ${libname}:: DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${libname}/cmake) install(FILES "${PROJECT_BINARY_DIR}/${libname}Config.cmake" "${PROJECT_BINARY_DIR}/${libname}ConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${libname}/cmake) install(DIRECTORY ${PROJECT_SOURCE_DIR}/cppy/include/cppy DESTINATION include) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") include(CPack) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/LICENSE0000644000175100001660000000271514752665560013124 0ustar00runnerdockerCopyright (c) 2014, Nucleic All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/MANIFEST.in0000644000175100001660000000055614752665560013656 0ustar00runnerdockerinclude MANIFEST.in include *.rst include LICENSE recursive-include cppy *.py recursive-include cppy *.h recursive-include docs/source *.rst recursive-include docs/source *.png recursive-include docs/source *.py recursive-include tests/ *.py include docs/make.bat include docs/Makefile recursive-include tests *.py prune .git prune dist prune build prune docs/build ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/PKG-INFO0000644000175100001660000000626014752665566013221 0ustar00runnerdockerMetadata-Version: 2.2 Name: cppy Version: 1.3.1 Author-email: The Nucleic Development Team Maintainer-email: "Matthieu C. Dartiailh" License: Copyright (c) 2014, Nucleic All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Project-URL: homepage, https://github.com/nucleic/cppy Project-URL: documentation, https://cppy.readthedocs.io/en/latest/ Project-URL: repository, https://github.com/nucleic/cppy Project-URL: changelog, https://github.com/nucleic/cppy/blob/main/releasenotes.rst Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.7 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: setuptools>=61.2 Cppy ==== .. image:: https://github.com/nucleic/cppy/actions/workflows/ci.yml/badge.svg :target: https://github.com/nucleic/cppy/actions/workflows/ci.yml A small C++ header library which makes it easier to write Python extension modules. The primary feature is a PyObject smart pointer which automatically handles reference counting and provides convenience methods for performing common object operations. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/README.rst0000644000175100001660000000063514752665560013605 0ustar00runnerdockerCppy ==== .. image:: https://github.com/nucleic/cppy/actions/workflows/ci.yml/badge.svg :target: https://github.com/nucleic/cppy/actions/workflows/ci.yml A small C++ header library which makes it easier to write Python extension modules. The primary feature is a PyObject smart pointer which automatically handles reference counting and provides convenience methods for performing common object operations. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4089956 cppy-1.3.1/cmake/0000755000175100001660000000000014752665566013200 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cmake/cppyConfig.cmake.in0000644000175100001660000000017514752665560016705 0ustar00runnerdocker@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4089956 cppy-1.3.1/cppy/0000755000175100001660000000000014752665566013073 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cppy/__init__.py0000644000175100001660000000745614752665560015212 0ustar00runnerdocker# -------------------------------------------------------------------------------------- # Copyright (c) 2014-2022, Nucleic Development Team. # # Distributed under the terms of the BSD 3-Clause License. # # The full license is in the file LICENSE, distributed with this software. # -------------------------------------------------------------------------------------- import os import sys from setuptools.command.build_ext import build_ext from .version import __version__, __version_info__ def get_include(): import os return os.path.join(os.path.dirname(__file__), "include") class CppyBuildExt(build_ext): """A custom build extension enforcing c++11 standard on all platforms. On Windows, FH4 Exception Handling can be disabled by setting the CPPY_DISABLE_FH4 environment variable. This avoids requiring VCRUNTIME140_1.dll """ # MSVC does not have a c++11 flag and default to c++14 anyway c_opts = {"msvc": ["/EHsc"], "unix": ["-std=c++11"]} def build_extensions(self): ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) cppy_includes = get_include() for ext in self.extensions: ext.include_dirs.insert(0, cppy_includes) ext.extra_compile_args = opts if sys.platform == "darwin": # Only Unix compilers and their ports have `compiler_so` so on MacOS # we can sure it will be present. compiler_cmd = self.compiler.compiler_so # Check if we are using Clang, accounting for absolute path if compiler_cmd is not None and "clang" in compiler_cmd[0]: # If so ensure we use a recent enough version of the stdlib ext.extra_compile_args.append("-stdlib=libc++") ext.extra_link_args.append("-stdlib=libc++") if ct == "msvc": # Switch to a static build for runtimes, but use dynamic # linking for `VCRUNTIME140.dll`, `VCRUNTIME140_1.dll`, and the UCRT. # This avoids requiring specific versions of `MSVCP140.dll`, while # keeping shared state with the rest of the Python process/extensions. is_debug = hasattr(sys, "gettotalrefcount") # only present in Python debug build # Mixing debug and release code is bad practice under Windows. The problem is that the # different versions can depend on different fundamental parts of the C++ runtime library, # such as how memory is allocated, structures for things like iterators might be # different, extra code could be generated to perform operations (e.g. checked iterators). # As a consequence we build as debug if python is built with debug symbols. debug_ext = "d" if is_debug else "" ext.extra_compile_args.append(f"/MT{debug_ext}") ext.extra_link_args.extend( [ f"ucrt{debug_ext}.lib", f"vcruntime{debug_ext}.lib", f"/nodefaultlib:libucrt{debug_ext}.lib", f"/nodefaultlib:libvcruntime{debug_ext}.lib", ] ) if os.environ.get("CPPY_DISABLE_FH4"): # Disable FH4 Exception Handling implementation so that we don't # require VCRUNTIME140_1.dll. For more details, see: # https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ # https://github.com/joerick/cibuildwheel/issues/423#issuecomment-677763904 ext.extra_compile_args.append("/d2FH4-") build_ext.build_extensions(self) __all__ = ["__version__", "__version_info__", "get_include", "CppyBuildExt"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4059956 cppy-1.3.1/cppy/include/0000755000175100001660000000000014752665566014516 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4099958 cppy-1.3.1/cppy/include/cppy/0000755000175100001660000000000014752665566015471 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cppy/include/cppy/cppy.h0000644000175100001660000000133014752665560016604 0ustar00runnerdocker/*----------------------------------------------------------------------------- | Copyright (c) 2014-2020, Nucleic | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once #define pyobject_cast( o ) ( reinterpret_cast( o ) ) #define pytype_cast( o ) ( reinterpret_cast( o ) ) // Used to cast PyDoc_STRVAR to void* in PyType_Slot #define cast_py_tp_doc( o ) ( reinterpret_cast( const_cast( o ) ) ) #define void_cast( o ) ( reinterpret_cast( o ) ) #include "defines.h" #include "errors.h" #include "ptr.h" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cppy/include/cppy/defines.h0000644000175100001660000000074514752665560017257 0ustar00runnerdocker/*----------------------------------------------------------------------------- | Copyright (c) 2014-2020, Nucleic | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once #include #define CPPY_MAJOR_VERSION 1 #define CPPY_MINOR_VERSION 1 #define CPPY_PATCH_VERSION 0 #define CPPY_VERSION "1.1.0" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cppy/include/cppy/errors.h0000644000175100001660000000261514752665560017154 0ustar00runnerdocker/*----------------------------------------------------------------------------- | Copyright (c) 2014-2020, Nucleic | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once #include namespace cppy { inline PyObject* system_error( const char* message ) { PyErr_SetString( PyExc_SystemError, message ); return 0; } inline PyObject* type_error( const char* message ) { PyErr_SetString( PyExc_TypeError, message ); return 0; } inline PyObject* type_error( PyObject* ob, const char* expected ) { PyErr_Format( PyExc_TypeError, "Expected object of type `%s`. Got object of type `%s` instead.", expected, Py_TYPE( ob )->tp_name ); return 0; } inline PyObject* value_error( const char* message ) { PyErr_SetString( PyExc_ValueError, message ); return 0; } inline PyObject* runtime_error( const char* message ) { PyErr_SetString( PyExc_RuntimeError, message ); return 0; } inline PyObject* attribute_error( const char* message ) { PyErr_SetString( PyExc_AttributeError, message ); return 0; } inline PyObject* attribute_error( PyObject* ob, const char* attr ) { PyErr_Format( PyExc_AttributeError, "'%s' object has no attribute '%s'", Py_TYPE( ob )->tp_name, attr ); return 0; } } // namespace cppy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/cppy/include/cppy/ptr.h0000644000175100001660000001752214752665560016450 0ustar00runnerdocker/*----------------------------------------------------------------------------- | Copyright (c) 2014-2020, Nucleic | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once #include #include #include "defines.h" namespace cppy { template inline T* incref( T* ob ) { Py_INCREF( ob ); return ob; } template inline T* xincref( T* ob ) { Py_XINCREF( ob ); return ob; } template inline T* decref( T* ob ) { Py_DECREF( ob ); return ob; } template inline T* xdecref( T* ob ) { Py_XDECREF( ob ); return ob; } template inline void clear( T** ob ) { T* temp = *ob; *ob = 0; Py_XDECREF( temp ); } template inline void replace( T** src, T* ob ) { T* temp = *src; *src = ob; Py_XINCREF( ob ); Py_XDECREF( temp ); } class ptr { public: ptr() : m_ob( 0 ) { } ptr( const ptr& other ) : m_ob( cppy::xincref( other.get() ) ) { } ptr( PyObject* ob, bool incref = false ) : m_ob( incref ? cppy::xincref( ob ) : ob ) { } ~ptr() { PyObject* temp = m_ob; m_ob = 0; Py_XDECREF( temp ); } ptr& operator=( PyObject* other ) { PyObject* temp = m_ob; m_ob = other; Py_XDECREF( temp ); return *this; } ptr& operator=( const ptr& other ) { PyObject* temp = m_ob; m_ob = other.get(); Py_XINCREF( m_ob ); Py_XDECREF( temp ); return *this; } PyObject* get() const { return m_ob; } void set( PyObject* ob, bool incref = false ) { PyObject* temp = m_ob; m_ob = incref ? cppy::xincref( ob ) : ob; Py_XDECREF( temp ); } void set( const ptr& other ) { PyObject* temp = m_ob; m_ob = other.get(); Py_XINCREF( m_ob ); Py_XDECREF( temp ); } PyObject* release() { PyObject* temp = m_ob; m_ob = 0; return temp; } operator void*() const { return static_cast( m_ob ); } bool is_none() const { return m_ob == Py_None; } bool is_true() const { return m_ob == Py_True; } bool is_false() const { return m_ob == Py_False; } bool is_bool() const { return is_true() || is_false(); } bool is_int() const { return PyLong_Check( m_ob ) != 0; } bool is_float() const { return PyFloat_Check( m_ob ) != 0; } bool is_list() const { return PyList_Check( m_ob ) != 0; } bool is_dict() const { return PyDict_Check( m_ob ) != 0; } bool is_set() const { return PySet_Check( m_ob ) != 0; } bool is_bytes() const { return PyBytes_Check( m_ob ) != 0; } bool is_str() const { return PyUnicode_Check( m_ob ) != 0; } bool is_unicode() const { return PyUnicode_Check( m_ob ) != 0; } bool is_callable() const { return PyCallable_Check( m_ob ) != 0; } bool is_iter() const { return PyIter_Check( m_ob ) != 0; } bool is_type( PyTypeObject* cls ) const { return PyObject_TypeCheck( m_ob, cls ) != 0; } int is_truthy() const { return PyObject_IsTrue( m_ob ); } int is_instance( PyObject* cls ) const { return PyObject_IsInstance( m_ob, cls ); } int is_instance( const ptr& cls ) const { return is_instance( cls.get() ); } int is_subclass( PyObject* cls ) const { return PyObject_IsSubclass( m_ob, cls ); } int is_subclass( const ptr& cls ) const { return is_subclass( cls.get() ); } PyObject* iter() const { return PyObject_GetIter( m_ob ); } PyObject* next() const { return PyIter_Next( m_ob ); } PyObject* repr() const { return PyObject_Repr( m_ob ); } PyObject* str() const { return PyObject_Str( m_ob ); } PyObject* bytes() const { return PyObject_Bytes( m_ob ); } PyObject* unicode() const { return PyObject_Str( m_ob ); } Py_ssize_t length() const { return PyObject_Length( m_ob ); } PyTypeObject* type() const { return Py_TYPE( m_ob ); } int richcmp( PyObject* other, int opid ) const { return PyObject_RichCompareBool( m_ob, other, opid ); } int richcmp( const ptr& other, int opid ) const { return richcmp( other.get(), opid ); } Py_hash_t hash() const { return PyObject_Hash( m_ob ); } bool hasattr( PyObject* attr ) const { return PyObject_HasAttr( m_ob, attr ) == 1; } bool hasattr( const ptr& attr ) const { return hasattr( attr.get() ); } bool hasattr( const char* attr ) const { return PyObject_HasAttrString( m_ob, attr ) == 1; } bool hasattr( const std::string& attr ) const { return hasattr( attr.c_str() ); } PyObject* getattr( PyObject* attr ) const { return PyObject_GetAttr( m_ob, attr ); } PyObject* getattr( const ptr& attr ) const { return getattr( attr.get() ); } PyObject* getattr( const char* attr ) const { return PyObject_GetAttrString( m_ob, attr ); } PyObject* getattr( const std::string& attr ) const { return getattr( attr.c_str() ); } bool setattr( PyObject* attr, PyObject* value ) const { return PyObject_SetAttr( m_ob, attr, value ) == 0; } bool setattr( const ptr& attr, PyObject* value ) const { return setattr( attr.get(), value ); } bool setattr( PyObject* attr, const ptr& value ) const { return setattr( attr, value.get() ); } bool setattr( const ptr& attr, const ptr& value ) const { return setattr( attr.get(), value.get() ); } bool setattr( const char* attr, PyObject* value ) const { return PyObject_SetAttrString( m_ob, attr, value ) == 0; } bool setattr( const char* attr, const ptr& value ) const { return setattr( attr, value.get() ); } bool setattr( const std::string& attr, PyObject* value ) const { return setattr( attr.c_str(), value ); } bool setattr( const std::string& attr, const ptr& value ) const { return setattr( attr.c_str(), value.get() ); } bool delattr( PyObject* attr ) const { return PyObject_DelAttr( m_ob, attr ) == 0; } bool delattr( const ptr& attr ) const { return delattr( attr.get() ); } bool delattr( const char* attr ) const { return PyObject_DelAttrString( m_ob, attr ) == 0; } bool delattr( const std::string& attr ) const { return delattr( attr.c_str() ); } PyObject* getitem( PyObject* key ) const { return PyObject_GetItem( m_ob, key ); } PyObject* getitem( const ptr& key ) const { return getitem( key.get() ); } bool setitem( PyObject* key, PyObject* value ) const { return PyObject_SetItem( m_ob, key, value ) == 0; } bool setitem( const ptr& key, PyObject* value ) const { return setitem( key.get(), value ); } bool setitem( PyObject* key, const ptr& value ) const { return setitem( key, value.get() ); } bool setitem( const ptr& key, const ptr& value ) const { return setitem( key.get(), value.get() ); } bool delitem( PyObject* key ) { return PyObject_DelItem( m_ob, key ) == 0; } bool delitem( const ptr& key ) { return delitem( key.get() ); } PyObject* call( PyObject* args, PyObject* kwargs = 0 ) const { return PyObject_Call( m_ob, args, kwargs ); } PyObject* call( const ptr& args ) const { return call( args.get() ); } PyObject* call( const ptr& args, const ptr& kwargs ) const { return call( args.get(), kwargs.get() ); } PyObject* call( const ptr& args, PyObject* kwargs ) const { return call( args.get(), kwargs ); } PyObject* call( PyObject* args, const ptr& kwargs ) const { return call( args, kwargs.get() ); } protected: PyObject* m_ob; }; inline bool operator!=( const ptr& lhs, const ptr& rhs ) { return lhs.get() != rhs.get(); } inline bool operator!=( PyObject* lhs, const ptr& rhs ) { return lhs != rhs.get(); } inline bool operator!=( const ptr& lhs, PyObject* rhs ) { return lhs.get() != rhs; } inline bool operator==( const ptr& lhs, const ptr& rhs ) { return lhs.get() == rhs.get(); } inline bool operator==( PyObject* lhs, const ptr& rhs ) { return lhs == rhs.get(); } inline bool operator==( const ptr& lhs, PyObject* rhs ) { return lhs.get() == rhs; } } // namespace cppy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy/version.py0000644000175100001660000000155014752665566015133 0ustar00runnerdocker# ----------------------------------------------------------------------------- # Copyright (c) 2014-2025, Nucleic Development Team. # # Distributed under the terms of the Modified BSD License. # # The full license is in the file LICENSE, distributed with this software. # ----------------------------------------------------------------------------- # This file is auto-generated by setuptools-scm do NOT edit it. from collections import namedtuple #: A namedtuple of the version info for the current release. _version_info = namedtuple("_version_info", "major minor micro status") parts = "1.3.1".split(".", 3) __version_info__ = _version_info( int(parts[0]), int(parts[1]), int(parts[2]), parts[3] if len(parts) == 4 else "", ) # Remove everything but the 'version_info' from this module. del namedtuple, _version_info, parts __version__ = "1.3.1" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/cppy.egg-info/0000755000175100001660000000000014752665566014565 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy.egg-info/PKG-INFO0000644000175100001660000000626014752665566015666 0ustar00runnerdockerMetadata-Version: 2.2 Name: cppy Version: 1.3.1 Author-email: The Nucleic Development Team Maintainer-email: "Matthieu C. Dartiailh" License: Copyright (c) 2014, Nucleic All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Project-URL: homepage, https://github.com/nucleic/cppy Project-URL: documentation, https://cppy.readthedocs.io/en/latest/ Project-URL: repository, https://github.com/nucleic/cppy Project-URL: changelog, https://github.com/nucleic/cppy/blob/main/releasenotes.rst Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.7 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: setuptools>=61.2 Cppy ==== .. image:: https://github.com/nucleic/cppy/actions/workflows/ci.yml/badge.svg :target: https://github.com/nucleic/cppy/actions/workflows/ci.yml A small C++ header library which makes it easier to write Python extension modules. The primary feature is a PyObject smart pointer which automatically handles reference counting and provides convenience methods for performing common object operations. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy.egg-info/SOURCES.txt0000644000175100001660000000126414752665566016454 0ustar00runnerdocker.gitignore .readthedocs.yaml CMakeLists.txt LICENSE MANIFEST.in README.rst pyproject.toml releasenotes.rst .github/workflows/ci.yml .github/workflows/docs.yml .github/workflows/release.yml cmake/cppyConfig.cmake.in cppy/__init__.py cppy/version.py cppy.egg-info/PKG-INFO cppy.egg-info/SOURCES.txt cppy.egg-info/dependency_links.txt cppy.egg-info/requires.txt cppy.egg-info/top_level.txt cppy/include/cppy/cppy.h cppy/include/cppy/defines.h cppy/include/cppy/errors.h cppy/include/cppy/ptr.h docs/Makefile docs/make.bat docs/requirements.txt docs/source/conf.py docs/source/errors.rst docs/source/index.rst docs/source/installation.rst docs/source/pointer.rst tests/test_getting_include_dir.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy.egg-info/dependency_links.txt0000644000175100001660000000000114752665566020633 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy.egg-info/requires.txt0000644000175100001660000000002114752665566017156 0ustar00runnerdockersetuptools>=61.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287414.0 cppy-1.3.1/cppy.egg-info/top_level.txt0000644000175100001660000000000514752665566017312 0ustar00runnerdockercppy ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1739287414.4109957 cppy-1.3.1/docs/0000755000175100001660000000000014752665566013050 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/Makefile0000644000175100001660000000111014752665560014473 0ustar00runnerdocker# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = source BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/make.bat0000644000175100001660000000142714752665560014453 0ustar00runnerdocker@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=source set BUILDDIR=build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/requirements.txt0000644000175100001660000000003514752665560016324 0ustar00runnerdockersphinx>=4 sphinx-rtd-theme>=1././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/docs/source/0000755000175100001660000000000014752665566014350 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/source/conf.py0000644000175100001660000001217614752665560015650 0ustar00runnerdocker# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) from cppy import __version__ # -- Project information ----------------------------------------------------- project = 'cppy' copyright = '2019-2024, Nucleic team' author = 'Nucleic team' # The full version, including alpha/beta/rc tags release = __version__ # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'cppydoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'cppy.tex', 'cppy Documentation', 'Nucleic team', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'cppy', 'cppy Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'cppy', 'cppy Documentation', author, 'cppy', 'One line description of project.', 'Miscellaneous'), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # -- Extension configuration ------------------------------------------------- ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/source/errors.rst0000644000175100001660000000172614752665560016416 0ustar00runnerdockerError reporting =============== In addition to `cppy::ptr`, cppy provides a set of convenience functions for reporting errors which all return a NULL pointer allowing them to be used as follows: .. code:: c++ PyObject* function( PyObject* obj ) { cppy::ptr obj_ptr( cppy::incref( obj ) ); if( !obj_ptr.is_bool() ) return type_error( obj_ptr.get(), "bool" ); return obj_ptr.get(); } Functions --------- Functions taking two arguments provide sensible pre-formatted error messages. .. code:: c++ inline PyObject* system_error( const char* message ) inline PyObject* type_error( const char* message ) inline PyObject* type_error( PyObject* ob, const char* expected ) inline PyObject* value_error( const char* message ) inline PyObject* runtime_error( const char* message ) inline PyObject* attribute_error( const char* message ) inline PyObject* attribute_error( PyObject* ob, const char* attr ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/source/index.rst0000644000175100001660000000116214752665560016203 0ustar00runnerdocker.. cppy documentation master file, created by sphinx-quickstart on Wed Mar 27 22:37:56 2019. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to Cppy's documentation! ================================ Cppy is a small C++ header library which makes it easier to write Python extension modules. The primary feature is a PyObject smart pointer which automatically handles reference counting and provides convenience methods for performing common object operations. .. toctree:: :maxdepth: 2 installation.rst pointer.rst errors.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/source/installation.rst0000644000175100001660000000505714752665560017604 0ustar00runnerdockerInstallation and use with setuptools ==================================== Since Cppy is nothing else than a collection of header that are only compiled when used, installing it is extremely straightforward using pip:: $ pip install cppy If you want to run the development version, you can install directly from GitHub:: $ pip install git+https://github.com/nucleic/cppy Using Cppy in an extensions --------------------------- To use Cppy in your extension (written in C++), you simply need to include it. .. code:: c++ #include Cppy includes Python.h so when including cppy.h you do not need to also include Python.h. Every functions, classes exposed by Cppy are stored in the `cppy` namespace. .. code:: c++ cppy::ptr obj_ptr( PyUnicode_FromString("test") ) Use with setuptools ------------------- Cppy is only needed during the installation step of the projects using it. When using a PEP 517 compatible build system, one can simply specify cppy as a build requirement in ```pyproject.toml``:: [build-system] requires = ["setuptools>=42", "wheel", "cppy>=1.2"] Which will ensure that cppy is available in setup.py allowing to import it at the top level of the module. This allows in particular to import ``CppyBuildExt`` which enforces the use of C++11 and provide access to the cppy headers. On Windows, FH4 Exception Handling can be disabled by setting the CPPY_DISABLE_FH4 environment variable. This avoids requiring VCRUNTIME140_1.dll In one is not using a PEP 517 compatible install, the following example setup.py script illustrates how to use Cppy without requiring it to be installed before `setup.py` is run. .. code:: python from setuptools import setup, Extension from setuptools.command.build_ext import build_ext ext_modules = [ Extension( 'project', ['module.cpp'], include_dirs=['.'], language='c++', ), ] class BuildExt(build_ext): def build_extensions(self): # Delayed import of cppy to let setup_requires install it if # necessary import cppy ct = self.compiler.compiler_type for ext in self.extensions: # cppy.get_include() collect the path of the header files ext.include_dirs.insert(0, cppy.get_include()) build_ext.build_extensions(self) setup( name='project', python_requires='>=3.5', setup_requires=['cppy'], ext_modules=ext_modules, cmdclass={'build_ext': BuildExt}, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/docs/source/pointer.rst0000644000175100001660000001743214752665560016563 0ustar00runnerdockerCppy smart pointer ================== CPython relies on reference counting to manage object lifetime. A large pitfall when writing C-extension is to properly handle increfing and decrefing the reference count. Cppy aims at simplifying this process by providing a smart pointer class. Before diving into the details of it helps lets start a CPython reference counting crash-course. CPython reference counting crash course --------------------------------------- Each object allocated by Python has a reference count, indicating how many times this object is 'used'. When the reference count of an object goes to zero, it is de-allocated. Outside of C extension, one does not need to manage the reference count manually. When a function part of Python C-API returns a Python object, it returns a pointer to it. At the time at which the function returns, the referenced object is live and its reference count is above zero. Depending of the function, you do not have the same responsibility with respect to that object reference count: - Owned references: Most functions return a ``new`` reference which means that you are responsible for decrefing the object reference count when you are done with it (basically the function increfed the object reference count before returning). In this situation you own a reference. - Borrowed reference: Some functions (``PyList_GetItem``, ``PyTuple_GetItem``, ``PyDict_GetItem``, ...) do not incref the object count before returning. In that case, you have only a borrowed reference, you are not responsible for decrefing the object reference count. Borrowed references allow to avoid the cost of increfing/decrefing which is nice. However since you do not own the reference, if the object referenced is removed from its owner (list, tuple for the above two mentioned functions) it may just disappear and your reference becomes invalid. This can cause issues. If the object should outlive the container, or the time it will spend in the container you have to incref it manually. Lets now discuss the convention when calling a function. When calling a function, the caller is expected to own a reference to each of the arguments passed to the callee. The callee does not own the references, it only borrows them. As a consequence, it should not decref the reference and if it needs to store the object, in for example a C structure, it should incref it. Note that this does not apply in general to Python container since those are manipulated using functions that take care of it. There are however some exceptions that steals a reference, meaning that you are not the owner of the reference after the call. ``PyList_SetItem``, for example, steal references. An easy way to get reference count wrong is forgetting to decref some intermediate object before leaving a function. This is particularly true if the function has some early exit point because an exception should be raised. A good practice is to have a single exit point, however it is not always possible/practical and even like this it is possible to miss references, this is typically where cppy can help. This is a very brief introduction to reference counting. You can read a bit more in the official `Python documentation`_ and in the `Python API`_ documentation. .. _Python documentation: https://docs.python.org/3/c-api/intro.html#objects-types-and-reference-counts .. _Python API: https://docs.python.org/3/c-api/refcounting.html Cppy smart pointer class ------------------------ Cppy smart pointer (``cppy::ptr``) can be initialized with a pointer to a Python object as follows: .. code:: c++ cppy::ptr obj_ptr( PyUnicode_FromString("test") ) When created, the class assume that you own the reference, if it is not the case you should incref it first: .. code:: c++ PyObject* function( PyObject* obj ) { cppy::ptr obj_ptr( cppy::incref( obj ) ); cppy::ptr obj_ptr2( obj, true ); } .. note:: Cppy provides convenient inline function for common reference manipulation: - ``cppy::incref``, ``cppy::xincref``, ``cppy::decref``, ``cppy::xdecref`` use the the similarly named Python macros and return the input value. - ``cppy::clear``, ``cppy::replace`` are similar but return void. You can also initialize a ``cppy::ptr`` from another ``cppy::ptr`` in which case the reference count will always be incremented. The main advantage provided by ``cppy::ptr`` is that it implements a destructor that will be invoked automatically by the c++ runtime when the ``cppy::ptr`` goes out of scope. The destructor will decref the reference for you. As a consequence you can be sure that your reference you always be decremented when you leave the function. Sometimes, however, that is not what you want, because you want to return the reference the ``cppy::ptr`` manage. You can request the ``cppy::ptr`` to give back the reference using its ``release`` method. Lets illustrate on a tiny example: .. code:: c++ PyObject* function( PyObject* obj ) { cppy::ptr repr_ptr( PyObject_Repr( obj ) ); return repr_ptr.release(); } Function which are part of Python C-API are not aware of of ``cppy::ptr`` and when calling them you need to provide the original ``PyObject*``. To access, you simply need to call the ``get`` method of the ``cppy::ptr`` object. .. code:: c++ PyObject* function( PyObject* obj ) { cppy::ptr l_ptr( PyList_New() ); if( PyList_Append( l_ptr.get(), obj ) != 0 ) return 0; return l_ptr.release(); } Here we see that because we use ``cppy::ptr`` to manage the list, we do not have to worry about decrefing the reference if an exception occurs, the runtime will do it for us. If no exception occurs, we stop managing the reference and we are good. Using cppy does not eliminate all the pitfalls of writing C-extensions. For example if you release too early (for example when passing the object to a function that may fail), you can still leak references. However it does alleviate some of the complexity. ``cppy::ptr`` methods --------------------- All methods that takes a ``PyObject*`` can also accept a ``cppy::ptr``. Most names should be self-explanatory, and apart from the is\_ methods most of them rely on the PyObject\_ functions similarly named: .. code:: c++ bool is_none() const bool is_true() const bool is_false() const bool is_bool() const bool is_int() const bool is_float() const bool is_list() const bool is_dict() const bool is_set() const bool is_bytes() const bool is_str() const bool is_unicode() const bool is_callable() const bool is_iter() const bool is_type( PyTypeObject* cls ) const int is_truthy() const int is_instance( PyObject* cls ) const int is_subclass( PyObject* cls ) const PyObject* iter() const PyObject* next() const PyObject* repr() const PyObject* str() const PyObject* bytes() const PyObject* unicode() const Py_ssize_t length() const PyTypeObject* type() const int richcmp( PyObject* other, int opid ) const long hash() const bool hasattr( PyObject* attr ) const bool hasattr( const char* attr ) const bool hasattr( const std::string& attr ) const PyObject* getattr( PyObject* attr ) const PyObject* getattr( const char* attr ) const PyObject* getattr( const std::string& attr ) const bool setattr( PyObject* attr, PyObject* value ) const bool setattr( const char* attr, PyObject* value ) const bool setattr( const std::string& attr, PyObject* value ) const bool delattr( PyObject* attr ) const bool delattr( const char* attr ) const bool delattr( const std::string& attr ) const PyObject* getitem( PyObject* key ) const bool setitem( PyObject* key, PyObject* value ) const bool delitem( PyObject* key ) PyObject* call( PyObject* args, PyObject* kwargs = 0 ) const ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/pyproject.toml0000644000175100001660000000512314752665560015027 0ustar00runnerdocker# -------------------------------------------------------------------------------------- # Copyright (c) 2014-2025, Nucleic Development Team. # # Distributed under the terms of the Modified BSD License. # # The full license is in the file LICENSE, distributed with this software. # -------------------------------------------------------------------------------------- [project] name = "cppy" readme = "README.rst" requires-python = ">=3.7" license = {file = "LICENSE"} authors = [ {name = "The Nucleic Development Team", email = "sccolbert@gmail.com"} ] maintainers = [ {name = "Matthieu C. Dartiailh", email = "m.dartiailh@gmail.com"} ] classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", ] dynamic=["version"] dependencies = ["setuptools>=61.2"] [project.urls] homepage = "https://github.com/nucleic/cppy" documentation = "https://cppy.readthedocs.io/en/latest/" repository = "https://github.com/nucleic/cppy" changelog = "https://github.com/nucleic/cppy/blob/main/releasenotes.rst" [build-system] requires = ["setuptools>=61.2", "wheel", "setuptools_scm[toml]>=3.4.3"] build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["cppy"] package-data = {cppy = ["include/cppy/*.h"]} [tool.setuptools_scm] write_to = "cppy/version.py" write_to_template = """ # ----------------------------------------------------------------------------- # Copyright (c) 2014-2025, Nucleic Development Team. # # Distributed under the terms of the Modified BSD License. # # The full license is in the file LICENSE, distributed with this software. # ----------------------------------------------------------------------------- # This file is auto-generated by setuptools-scm do NOT edit it. from collections import namedtuple #: A namedtuple of the version info for the current release. _version_info = namedtuple("_version_info", "major minor micro status") parts = "{version}".split(".", 3) __version_info__ = _version_info( int(parts[0]), int(parts[1]), int(parts[2]), parts[3] if len(parts) == 4 else "", ) # Remove everything but the 'version_info' from this module. del namedtuple, _version_info, parts __version__ = "{version}" """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/releasenotes.rst0000644000175100001660000000163414752665560015341 0ustar00runnerdockerCppy Release Notes ================== 1.3.1 - 11/02/2025 ------------------- - on Windows link to debug runtime lib when building with a debug Python PR #29 1.3.0 - 26/11/2024 ------------------ - prevent linking dynamically to MSVCP on windows 1.2.1 - 03/30/2022 ------------------ - make the pyproject.toml fully PEP 621 compliant PR #19 1.2.0 - 03/11/2022 ------------------ - expose a build_ext subclass that can be re-used in other projects PR #16 - use a PEP 517 compatible install procedure PR #16 - do not access directly ob_type on PyObject use Py_TYPE 1.1.0 - 06/25/2020 ------------------ - drop Python 2 support PR #3 - add documentation and tests PR #3 - add cast_py_tp_doc to cast "cleanly" to void* for use in PyType_Slot 1.0.2 - 09/28/2014 ------------------ - update license header 1.0.1 - 09/28/2014 ------------------ - fix Python 3 int check 1.0.0 - 09/24/2014 ------------------ - first release ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/setup.cfg0000644000175100001660000000004614752665566013741 0ustar00runnerdocker[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1739287414.411996 cppy-1.3.1/tests/0000755000175100001660000000000014752665566013262 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1739287408.0 cppy-1.3.1/tests/test_getting_include_dir.py0000644000175100001660000000101614752665560020665 0ustar00runnerdocker#------------------------------------------------------------------------------ # Copyright (c) 2014-2019, Nucleic # # Distributed under the terms of the BSD 3-Clause License. # # The full license is in the file LICENSE, distributed with this software. #------------------------------------------------------------------------------ """Test getting the include directory. """ def test_getting_include_directory(): """Test getting the include directory. """ from cppy import get_include assert get_include()