pax_global_header00006660000000000000000000000064146504424350014521gustar00rootroot0000000000000052 comment=cf614527948b86845b781c160bdc25c05aef6151 cymem-release-v2.0.10/000077500000000000000000000000001465044243500145175ustar00rootroot00000000000000cymem-release-v2.0.10/.github/000077500000000000000000000000001465044243500160575ustar00rootroot00000000000000cymem-release-v2.0.10/.github/workflows/000077500000000000000000000000001465044243500201145ustar00rootroot00000000000000cymem-release-v2.0.10/.github/workflows/cibuildwheel.yml000066400000000000000000000050221465044243500232760ustar00rootroot00000000000000name: Build on: push: tags: - 'release-v[0-9]+.[0-9]+.[0-9]+' jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: # macos-13 is an intel runner, macos-14 is apple silicon os: [ubuntu-latest, windows-latest, macos-13] steps: - uses: actions/checkout@v4 - name: Build wheels uses: pypa/cibuildwheel@v2.19.1 env: CIBW_SOME_OPTION: value with: package-dir: . output-dir: wheelhouse config-file: "{package}/pyproject.toml" - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build sdist run: pipx run build --sdist - uses: actions/upload-artifact@v4 with: name: cibw-sdist path: dist/*.tar.gz create_release: needs: [build_wheels, build_sdist] runs-on: ubuntu-latest permissions: contents: write checks: write actions: read issues: read packages: write pull-requests: read repository-projects: read statuses: read steps: - name: Get the tag name and determine if it's a prerelease id: get_tag_info run: | FULL_TAG=${GITHUB_REF#refs/tags/} if [[ $FULL_TAG == release-* ]]; then TAG_NAME=${FULL_TAG#release-} IS_PRERELEASE=false elif [[ $FULL_TAG == prerelease-* ]]; then TAG_NAME=${FULL_TAG#prerelease-} IS_PRERELEASE=true else echo "Tag does not match expected patterns" >&2 exit 1 fi echo "FULL_TAG=$TAG_NAME" >> $GITHUB_ENV echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV echo "IS_PRERELEASE=$IS_PRERELEASE" >> $GITHUB_ENV - uses: actions/download-artifact@v4 with: # unpacks all CIBW artifacts into dist/ pattern: cibw-* path: dist merge-multiple: true - name: Create Draft Release id: create_release uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: name: ${{ env.TAG_NAME }} draft: true prerelease: ${{ env.IS_PRERELEASE }} files: "./dist/*" cymem-release-v2.0.10/.github/workflows/publish_pypi.yml000066400000000000000000000016601465044243500233510ustar00rootroot00000000000000# The cibuildwheel action triggers on creation of a release, this # triggers on publication. # The expected workflow is to create a draft release and let the wheels # upload, and then hit 'publish', which uploads to PyPi. on: release: types: - published jobs: upload_pypi: runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/cymem permissions: id-token: write contents: read if: github.event_name == 'release' && github.event.action == 'published' # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this) # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') steps: - uses: robinraju/release-downloader@v1 with: tag: ${{ github.event.release.tag_name }} fileName: '*' out-file-path: 'dist' - uses: pypa/gh-action-pypi-publish@release/v1 cymem-release-v2.0.10/.github/workflows/tests.yml000066400000000000000000000037721465044243500220120ustar00rootroot00000000000000name: tests on: push: tags-ignore: - '**' paths-ignore: - "*.md" - ".github/*" pull_request: types: [opened, synchronize, reopened, edited] paths-ignore: - "*.md" env: MODULE_NAME: 'cymem' RUN_MYPY: 'false' jobs: tests: name: Test if: github.repository_owner == 'explosion' strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python_version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] include: - os: windows-2019 python_version: "3.6" - os: ubuntu-20.04 python_version: "3.6" runs-on: ${{ matrix.os }} steps: - name: Check out repo uses: actions/checkout@v3 - name: Configure Python version uses: actions/setup-python@v4 with: python-version: ${{ matrix.python_version }} architecture: x64 - name: Build sdist run: | python -m pip install -U build pip setuptools python -m pip install -U -r requirements.txt python -m build --sdist - name: Run mypy shell: bash if: ${{ env.RUN_MYPY == 'true' }} run: | python -m mypy $MODULE_NAME - name: Delete source directory shell: bash run: | rm -rf $MODULE_NAME - name: Uninstall all packages run: | python -m pip freeze > installed.txt python -m pip uninstall -y -r installed.txt - name: Install from sdist shell: bash run: | SDIST=$(python -c "import os;print(os.listdir('./dist')[-1])" 2>&1) pip install dist/$SDIST - name: Test import shell: bash run: | python -c "import $MODULE_NAME" -Werror - name: Install test requirements run: | python -m pip install -U -r requirements.txt - name: Run tests shell: bash run: | python -m pytest --pyargs $MODULE_NAME -Werror cymem-release-v2.0.10/.gitignore000066400000000000000000000016121465044243500165070ustar00rootroot00000000000000# Cython / C extensions cythonize.json spacy/*.html *.cpp *.so # Vim / VSCode / editors *.swp *.sw* Profile.prof .vscode .sass-cache # Python .Python .python-version __pycache__/ .pytest_cache *.py[cod] .env/ .env* .~env/ .venv venv/ .dev .denv .pypyenv .pytest_cache/ # Distribution / packaging env/ build/ develop-eggs/ dist/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ pip-wheel-metadata/ Pipfile.lock .installed.cfg *.egg .eggs MANIFEST # Temporary files *.~* tmp/ # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Rope .ropeproject # Django stuff: *.log *.pot # Windows *.bat Thumbs.db Desktop.ini # Mac OS X *.DS_Store # Komodo project files *.komodoproject # Other *.tgz # Pycharm project files *.idea cymem-release-v2.0.10/LICENSE000066400000000000000000000021271465044243500155260ustar00rootroot00000000000000The MIT License (MIT) Copyright (C) 2016-2022 ExplosionAI GmbH, 2014 Matthew Honnibal 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. cymem-release-v2.0.10/MANIFEST.in000066400000000000000000000001001465044243500162440ustar00rootroot00000000000000include LICENSE include README.md recursive-exclude cymem *.cpp cymem-release-v2.0.10/README.md000066400000000000000000000156161465044243500160070ustar00rootroot00000000000000 # cymem: A Cython Memory Helper cymem provides two small memory-management helpers for Cython. They make it easy to tie memory to a Python object's life-cycle, so that the memory is freed when the object is garbage collected. [![tests](https://github.com/explosion/cymem/actions/workflows/tests.yml/badge.svg)](https://github.com/explosion/cymem/actions/workflows/tests.yml) [![pypi Version](https://img.shields.io/pypi/v/cymem.svg?style=flat-square&logo=pypi&logoColor=white)](https://pypi.python.org/pypi/cymem) [![conda Version](https://img.shields.io/conda/vn/conda-forge/cymem.svg?style=flat-square&logo=conda-forge&logoColor=white)](https://anaconda.org/conda-forge/cymem) [![Python wheels](https://img.shields.io/badge/wheels-%E2%9C%93-4c1.svg?longCache=true&style=flat-square&logo=python&logoColor=white)](https://github.com/explosion/wheelwright/releases) ## Overview The most useful is `cymem.Pool`, which acts as a thin wrapper around the calloc function: ```python from cymem.cymem cimport Pool cdef Pool mem = Pool() data1 = mem.alloc(10, sizeof(int)) data2 = mem.alloc(12, sizeof(float)) ``` The `Pool` object saves the memory addresses internally, and frees them when the object is garbage collected. Typically you'll attach the `Pool` to some cdef'd class. This is particularly handy for deeply nested structs, which have complicated initialization functions. Just pass the `Pool` object into the initializer, and you don't have to worry about freeing your struct at all — all of the calls to `Pool.alloc` will be automatically freed when the `Pool` expires. ## Installation Installation is via [pip](https://pypi.python.org/pypi/pip), and requires [Cython](http://cython.org). Before installing, make sure that your `pip`, `setuptools` and `wheel` are up to date. ```bash pip install -U pip setuptools wheel pip install cymem ``` ## Example Use Case: An array of structs Let's say we want a sequence of sparse matrices. We need fast access, and a Python list isn't performing well enough. So, we want a C-array or C++ vector, which means we need the sparse matrix to be a C-level struct — it can't be a Python class. We can write this easily enough in Cython: ```python """Example without Cymem To use an array of structs, we must carefully walk the data structure when we deallocate it. """ from libc.stdlib cimport calloc, free cdef struct SparseRow: size_t length size_t* indices double* values cdef struct SparseMatrix: size_t length SparseRow* rows cdef class MatrixArray: cdef size_t length cdef SparseMatrix** matrices def __cinit__(self, list py_matrices): self.length = 0 self.matrices = NULL def __init__(self, list py_matrices): self.length = len(py_matrices) self.matrices = calloc(len(py_matrices), sizeof(SparseMatrix*)) for i, py_matrix in enumerate(py_matrices): self.matrices[i] = sparse_matrix_init(py_matrix) def __dealloc__(self): for i in range(self.length): sparse_matrix_free(self.matrices[i]) free(self.matrices) cdef SparseMatrix* sparse_matrix_init(list py_matrix) except NULL: sm = calloc(1, sizeof(SparseMatrix)) sm.length = len(py_matrix) sm.rows = calloc(sm.length, sizeof(SparseRow)) cdef size_t i, j cdef dict py_row cdef size_t idx cdef double value for i, py_row in enumerate(py_matrix): sm.rows[i].length = len(py_row) sm.rows[i].indices = calloc(sm.rows[i].length, sizeof(size_t)) sm.rows[i].values = calloc(sm.rows[i].length, sizeof(double)) for j, (idx, value) in enumerate(py_row.items()): sm.rows[i].indices[j] = idx sm.rows[i].values[j] = value return sm cdef void* sparse_matrix_free(SparseMatrix* sm) except *: cdef size_t i for i in range(sm.length): free(sm.rows[i].indices) free(sm.rows[i].values) free(sm.rows) free(sm) ``` We wrap the data structure in a Python ref-counted class at as low a level as we can, given our performance constraints. This allows us to allocate and free the memory in the `__cinit__` and `__dealloc__` Cython special methods. However, it's very easy to make mistakes when writing the `__dealloc__` and `sparse_matrix_free` functions, leading to memory leaks. cymem prevents you from writing these deallocators at all. Instead, you write as follows: ```python """Example with Cymem. Memory allocation is hidden behind the Pool class, which remembers the addresses it gives out. When the Pool object is garbage collected, all of its addresses are freed. We don't need to write MatrixArray.__dealloc__ or sparse_matrix_free, eliminating a common class of bugs. """ from cymem.cymem cimport Pool cdef struct SparseRow: size_t length size_t* indices double* values cdef struct SparseMatrix: size_t length SparseRow* rows cdef class MatrixArray: cdef size_t length cdef SparseMatrix** matrices cdef Pool mem def __cinit__(self, list py_matrices): self.mem = None self.length = 0 self.matrices = NULL def __init__(self, list py_matrices): self.mem = Pool() self.length = len(py_matrices) self.matrices = self.mem.alloc(self.length, sizeof(SparseMatrix*)) for i, py_matrix in enumerate(py_matrices): self.matrices[i] = sparse_matrix_init(self.mem, py_matrix) cdef SparseMatrix* sparse_matrix_init_cymem(Pool mem, list py_matrix) except NULL: sm = mem.alloc(1, sizeof(SparseMatrix)) sm.length = len(py_matrix) sm.rows = mem.alloc(sm.length, sizeof(SparseRow)) cdef size_t i, j cdef dict py_row cdef size_t idx cdef double value for i, py_row in enumerate(py_matrix): sm.rows[i].length = len(py_row) sm.rows[i].indices = mem.alloc(sm.rows[i].length, sizeof(size_t)) sm.rows[i].values = mem.alloc(sm.rows[i].length, sizeof(double)) for j, (idx, value) in enumerate(py_row.items()): sm.rows[i].indices[j] = idx sm.rows[i].values[j] = value return sm ``` All that the `Pool` class does is remember the addresses it gives out. When the `MatrixArray` object is garbage-collected, the `Pool` object will also be garbage collected, which triggers a call to `Pool.__dealloc__`. The `Pool` then frees all of its addresses. This saves you from walking back over your nested data structures to free them, eliminating a common class of errors. ## Custom Allocators Sometimes external C libraries use private functions to allocate and free objects, but we'd still like the laziness of the `Pool`. ```python from cymem.cymem cimport Pool, WrapMalloc, WrapFree cdef Pool mem = Pool(WrapMalloc(priv_malloc), WrapFree(priv_free)) ``` cymem-release-v2.0.10/bin/000077500000000000000000000000001465044243500152675ustar00rootroot00000000000000cymem-release-v2.0.10/bin/push-tag.sh000077500000000000000000000005041465044243500173550ustar00rootroot00000000000000#!/usr/bin/env bash set -e # Insist repository is clean git diff-index --quiet HEAD git checkout master git pull origin master git push origin master version=$(grep "__version__ = " cymem/about.py) version=${version/__version__ = } version=${version/\'/} version=${version/\'/} git tag "v$version" git push origin --tags cymem-release-v2.0.10/cymem/000077500000000000000000000000001465044243500156315ustar00rootroot00000000000000cymem-release-v2.0.10/cymem/__init__.pxd000066400000000000000000000000001465044243500200730ustar00rootroot00000000000000cymem-release-v2.0.10/cymem/__init__.py000066400000000000000000000000251465044243500177370ustar00rootroot00000000000000from .about import * cymem-release-v2.0.10/cymem/about.py000066400000000000000000000003521465044243500173150ustar00rootroot00000000000000__title__ = "cymem" __version__ = "2.0.9a3" __summary__ = "Manage calls to calloc/free through Cython" __uri__ = "https://github.com/explosion/cymem" __author__ = "Matthew Honnibal" __email__ = "matt@explosion.ai" __license__ = "MIT" cymem-release-v2.0.10/cymem/cymem.pxd000066400000000000000000000014131465044243500174570ustar00rootroot00000000000000ctypedef void* (*malloc_t)(size_t n) ctypedef void (*free_t)(void *p) cdef class PyMalloc: cdef malloc_t malloc cdef void _set(self, malloc_t malloc) cdef PyMalloc WrapMalloc(malloc_t malloc) cdef class PyFree: cdef free_t free cdef void _set(self, free_t free) cdef PyFree WrapFree(free_t free) cdef class Pool: cdef readonly size_t size cdef readonly dict addresses cdef readonly list refs cdef readonly PyMalloc pymalloc cdef readonly PyFree pyfree cdef void* alloc(self, size_t number, size_t size) except NULL cdef void free(self, void* addr) except * cdef void* realloc(self, void* addr, size_t n) except NULL cdef class Address: cdef void* ptr cdef readonly PyMalloc pymalloc cdef readonly PyFree pyfree cymem-release-v2.0.10/cymem/cymem.pyx000066400000000000000000000127301465044243500175100ustar00rootroot00000000000000# cython: embedsignature=True from cpython.mem cimport PyMem_Malloc, PyMem_Free from cpython.ref cimport Py_INCREF, Py_DECREF from libc.string cimport memset from libc.string cimport memcpy import warnings WARN_ZERO_ALLOC = False cdef class PyMalloc: cdef void _set(self, malloc_t malloc): self.malloc = malloc cdef PyMalloc WrapMalloc(malloc_t malloc): cdef PyMalloc o = PyMalloc() o._set(malloc) return o cdef class PyFree: cdef void _set(self, free_t free): self.free = free cdef PyFree WrapFree(free_t free): cdef PyFree o = PyFree() o._set(free) return o Default_Malloc = WrapMalloc(PyMem_Malloc) Default_Free = WrapFree(PyMem_Free) cdef class Pool: """Track allocated memory addresses, and free them all when the Pool is garbage collected. This provides an easy way to avoid memory leaks, and removes the need for deallocation functions for complicated structs. >>> from cymem.cymem cimport Pool >>> cdef Pool mem = Pool() >>> data1 = mem.alloc(10, sizeof(int)) >>> data2 = mem.alloc(12, sizeof(float)) Attributes: size (size_t): The current size (in bytes) allocated by the pool. addresses (dict): The currently allocated addresses and their sizes. Read-only. pymalloc (PyMalloc): The allocator to use (default uses PyMem_Malloc). pyfree (PyFree): The free to use (default uses PyMem_Free). """ def __cinit__(self, PyMalloc pymalloc=Default_Malloc, PyFree pyfree=Default_Free): self.size = 0 self.addresses = {} self.refs = [] self.pymalloc = pymalloc self.pyfree = pyfree def __dealloc__(self): cdef size_t addr if self.addresses is not None: for addr in self.addresses: if addr != 0: self.pyfree.free(addr) cdef void* alloc(self, size_t number, size_t elem_size) except NULL: """Allocate a 0-initialized number*elem_size-byte block of memory, and remember its address. The block will be freed when the Pool is garbage collected. Throw warning when allocating zero-length size and WARN_ZERO_ALLOC was set to True. """ if WARN_ZERO_ALLOC and (number == 0 or elem_size == 0): warnings.warn("Allocating zero bytes") cdef void* p = self.pymalloc.malloc(number * elem_size) if p == NULL: raise MemoryError("Error assigning %d bytes" % (number * elem_size)) memset(p, 0, number * elem_size) self.addresses[p] = number * elem_size self.size += number * elem_size return p cdef void* realloc(self, void* p, size_t new_size) except NULL: """Resizes the memory block pointed to by p to new_size bytes, returning a non-NULL pointer to the new block. new_size must be larger than the original. If p is not in the Pool or new_size is 0, a MemoryError is raised. """ if p not in self.addresses: raise ValueError("Pointer %d not found in Pool %s" % (p, self.addresses)) if new_size == 0: raise ValueError("Realloc requires new_size > 0") assert new_size > self.addresses[p] cdef void* new_ptr = self.alloc(1, new_size) if new_ptr == NULL: raise MemoryError("Error reallocating to %d bytes" % new_size) memcpy(new_ptr, p, self.addresses[p]) self.free(p) self.addresses[new_ptr] = new_size return new_ptr cdef void free(self, void* p) except *: """Frees the memory block pointed to by p, which must have been returned by a previous call to Pool.alloc. You don't necessarily need to free memory addresses manually --- you can instead let the Pool be garbage collected, at which point all the memory will be freed. If p is not in Pool.addresses, a KeyError is raised. """ self.size -= self.addresses.pop(p) self.pyfree.free(p) def own_pyref(self, object py_ref): self.refs.append(py_ref) cdef class Address: """A block of number * size-bytes of 0-initialized memory, tied to a Python ref-counted object. When the object is garbage collected, the memory is freed. >>> from cymem.cymem cimport Address >>> cdef Address address = Address(10, sizeof(double)) >>> d10 = address.ptr Args: number (size_t): The number of elements in the memory block. elem_size (size_t): The size of each element. Attributes: ptr (void*): Pointer to the memory block. addr (size_t): Read-only size_t cast of the pointer. pymalloc (PyMalloc): The allocator to use (default uses PyMem_Malloc). pyfree (PyFree): The free to use (default uses PyMem_Free). """ def __cinit__(self, size_t number, size_t elem_size, PyMalloc pymalloc=Default_Malloc, PyFree pyfree=Default_Free): self.ptr = NULL self.pymalloc = pymalloc self.pyfree = pyfree def __init__(self, size_t number, size_t elem_size): self.ptr = self.pymalloc.malloc(number * elem_size) if self.ptr == NULL: raise MemoryError("Error assigning %d bytes" % number * elem_size) memset(self.ptr, 0, number * elem_size) property addr: def __get__(self): return self.ptr def __dealloc__(self): if self.ptr != NULL: self.pyfree.free(self.ptr) cymem-release-v2.0.10/cymem/tests/000077500000000000000000000000001465044243500167735ustar00rootroot00000000000000cymem-release-v2.0.10/cymem/tests/__init__.py000066400000000000000000000000001465044243500210720ustar00rootroot00000000000000cymem-release-v2.0.10/cymem/tests/test_import.py000066400000000000000000000004071465044243500217170ustar00rootroot00000000000000# This is a very weak test, but testing Cython code can be hard. So, at least check # we can create the object... from cymem.cymem import Pool, Address def test_pool(): mem = Pool() assert mem.size == 0 def test_address(): address = Address(1, 2) cymem-release-v2.0.10/pyproject.toml000066400000000000000000000001541465044243500174330ustar00rootroot00000000000000[build-system] requires = [ "setuptools", "cython>=0.25", ] build-backend = "setuptools.build_meta" cymem-release-v2.0.10/requirements.txt000066400000000000000000000000241465044243500177770ustar00rootroot00000000000000cython>=0.25 pytest cymem-release-v2.0.10/setup.py000077500000000000000000000106351465044243500162410ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import io import os import sys import contextlib from setuptools import Extension, setup, find_packages from setuptools.command.build_ext import build_ext from sysconfig import get_path from Cython.Build import cythonize PACKAGES = find_packages() MOD_NAMES = ["cymem.cymem"] # By subclassing build_extensions we have the actual compiler that will be used which is really known only after finalize_options # http://stackoverflow.com/questions/724664/python-distutils-how-to-get-a-compiler-that-is-going-to-be-used compile_options = { "msvc": ["/Ox", "/EHsc"], "other": ["-O3", "-Wno-strict-prototypes", "-Wno-unused-function"], } link_options = {"msvc": [], "other": []} class build_ext_options: def build_options(self): for e in self.extensions: e.extra_compile_args = compile_options.get( self.compiler.compiler_type, compile_options["other"] ) for e in self.extensions: e.extra_link_args = link_options.get( self.compiler.compiler_type, link_options["other"] ) class build_ext_subclass(build_ext, build_ext_options): def build_extensions(self): build_ext_options.build_options(self) build_ext.build_extensions(self) def clean(path): for name in MOD_NAMES: name = name.replace(".", "/") for ext in [".so", ".html", ".cpp", ".c"]: file_path = os.path.join(path, name + ext) if os.path.exists(file_path): os.unlink(file_path) @contextlib.contextmanager def chdir(new_dir): old_dir = os.getcwd() try: os.chdir(new_dir) sys.path.insert(0, new_dir) yield finally: del sys.path[0] os.chdir(old_dir) def setup_package(): root = os.path.abspath(os.path.dirname(__file__)) if len(sys.argv) > 1 and sys.argv[1] == "clean": return clean(root) with chdir(root): with io.open(os.path.join(root, "cymem", "about.py"), encoding="utf8") as f: about = {} exec(f.read(), about) with io.open(os.path.join(root, "README.md"), encoding="utf8") as f: readme = f.read() include_dirs = [get_path("include")] ext_modules = [] for mod_name in MOD_NAMES: mod_path = mod_name.replace(".", "/") + ".pyx" ext_modules.append( Extension( mod_name, [mod_path], language="c++", include_dirs=include_dirs ) ) setup( name="cymem", zip_safe=False, packages=PACKAGES, package_data={"": ["*.pyx", "*.pxd"]}, description=about["__summary__"], long_description=readme, long_description_content_type="text/markdown", author=about["__author__"], author_email=about["__email__"], version=about["__version__"], url=about["__uri__"], license=about["__license__"], ext_modules=cythonize(ext_modules, language_level=2), setup_requires=["cython>=0.25"], classifiers=[ "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Programming Language :: Cython", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "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", "Topic :: Scientific/Engineering", ], cmdclass={"build_ext": build_ext_subclass}, ) if __name__ == "__main__": setup_package()