diff --git a/.github/workflows/pip-test.yml b/.github/workflows/pip-test.yml index a4541f1..adf39f7 100644 --- a/.github/workflows/pip-test.yml +++ b/.github/workflows/pip-test.yml @@ -1,4 +1,5 @@ -# Testing on linux, windows, macos, for python versions 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 +# Testing on linux, windows, macos, for python versions 3.8, 3.9, 3.10, 3.11, 3.12 +# Note: Python 3.7 support was dropped as it reached End of Life (EOL) on June 27, 2023 name: Test pip install @@ -8,20 +9,19 @@ on: jobs: build: - strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: ["ubuntu-latest", "windows-latest", "macos-latest"] runs-on: ${{ matrix.os }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Version check @@ -32,5 +32,6 @@ jobs: pip install "Cython>=0.29.23" "coveralls" - name: Install package run: | - python setup.py sdist - pip install dist/py-stringmatching-0.4.6.tar.gz + pip install build + python -m build --sdist + python -c "import glob, subprocess, sys; files = glob.glob('dist/*.tar.gz'); assert files, 'No .tar.gz files found in dist/'; subprocess.check_call([sys.executable, '-m', 'pip', 'install'] + files)" diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index d58cf46..11f5dc4 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,4 +1,5 @@ -# Testing on linux, windows, macos, for python versions 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 +# Testing on linux, windows, macos, for python versions 3.8, 3.9, 3.10, 3.11, 3.12 +# Note: Python 3.7 support was dropped as it reached End of Life (EOL) on June 27, 2023 name: Unit testing @@ -8,20 +9,19 @@ on: jobs: build: - strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: ["ubuntu-latest", "windows-latest", "macos-latest"] runs-on: ${{ matrix.os }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Version check @@ -31,7 +31,7 @@ jobs: python -m pip install --upgrade pip pip install "numpy<2.0" "Cython>=0.29.23" "coveralls" - name: Install package - run: python setup.py build_ext --inplace + run: pip install -e . - name: Run tests run: | python -m unittest -v diff --git a/CHANGES.txt b/CHANGES.txt index 0070d88..53a4a89 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +v0.4.7 - 2/14/2026 + * Migrated build to pyproject.toml (PEP 517/621); setup.py retained for Cython extensions only. + * Dropped custom build_ext and setuptools auto-install; use numpy.get_include() for extension builds. + * Dropped Testing support for Python 3.7 as it reached End of Life (EOL) on June 27, 2023. + v0.4.6 - 7/5/2024 * Limited Numpy to <2.0 in setup.py, due to compatibility issues * Added preliminary testing of pip install to Github Actions workflow diff --git a/MANIFEST.in b/MANIFEST.in index 505f54d..71bdf80 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,5 @@ include README.rst include CHANGES.txt include requirements.txt include LICENSE +include pyproject.toml recursive-include LICENSES * diff --git a/README.rst b/README.rst index 6e86ac6..aa66fcc 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,8 @@ Important links Dependencies ============ -py_stringmatching has been tested on each Python version between 3.7 and 3.12, inclusive. +py_stringmatching has been tested on each Python version between 3.8 and 3.12, inclusive. +Note: Python 3.7 support was dropped as it reached End of Life (EOL) on June 27, 2023. The required dependencies to build the package are NumPy 1.7.0 or higher, but lower than 2.0, and a C or C++ compiler. For the development version, you will also need Cython. diff --git a/docs/Installation.rst b/docs/Installation.rst index a7e6178..2c70151 100644 --- a/docs/Installation.rst +++ b/docs/Installation.rst @@ -4,7 +4,7 @@ Installation Requirements ------------ - * Python 3.7-3.11 + * Python 3.8-3.11 (Python 3.7 support was dropped as it reached End of Life (EOL) on June 27, 2023) * C or C++ compiler (parts of the package are in Cython for efficiency reasons, and you need C or C++ compiler to compile these parts) Platforms diff --git a/py_stringmatching/__init__.py b/py_stringmatching/__init__.py index 6cba7d5..c4c5b72 100644 --- a/py_stringmatching/__init__.py +++ b/py_stringmatching/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.4.6" +__version__ = "0.4.7" # Import tokenizers from py_stringmatching.tokenizer.alphabetic_tokenizer import AlphabeticTokenizer diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e3317e9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,55 @@ +[build-system] +requires = [ + "setuptools>=64", + "wheel", + "Cython>=0.29", + "numpy>=1.7.0,<2.0" +] +build-backend = "setuptools.build_meta" + +[project] +name = "py-stringmatching" +version = "0.4.7" +description = "Python library for string matching." +readme = "README.rst" +license = {text = "BSD"} +authors = [ + {name = "UW Magellan Team", email = "uwmagellan@gmail.com"} +] +requires-python = ">=3.7" +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Intended Audience :: Education", + "License :: OSI Approved :: BSD License", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "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", + "Topic :: Scientific/Engineering", + "Topic :: Utilities", + "Topic :: Software Development :: Libraries", +] +dependencies = [ + "numpy>=1.7.0,<2.0", +] + +[project.urls] +Homepage = "https://sites.google.com/site/anhaidgroup/projects/magellan/py_stringmatching" + +[tool.setuptools] +include-package-data = true +zip-safe = false + +[tool.setuptools.packages.find] +exclude = ["benchmarks", "benchmarks.custom_benchmarks"] \ No newline at end of file diff --git a/setup.py b/setup.py index f203492..ccea942 100644 --- a/setup.py +++ b/setup.py @@ -1,148 +1,94 @@ -import subprocess -import sys +""" +Minimal setup.py for building Cython extensions. +Most configuration is now in pyproject.toml. +""" import os +import sys +from setuptools import setup, Extension -# check if pip is installed. If not, raise an ImportError -PIP_INSTALLED = True - -try: - import pip -except ImportError: - PIP_INSTALLED = False - -def install_and_import(package): - import importlib - try: - importlib.import_module(package) - except ImportError: - if not PIP_INSTALLED: - raise ImportError('pip is not installed.') - pip.main(['install', package]) - finally: - globals()[package] = importlib.import_module(package) - -# check if setuptools is installed. If not, install setuptools -# automatically using pip. -install_and_import('setuptools') - -from setuptools.command.build_ext import build_ext as _build_ext - -class build_ext(_build_ext): - def build_extensions(self): - import pkg_resources - numpy_incl = pkg_resources.resource_filename('numpy', 'core/include') - - for ext in self.extensions: - if (hasattr(ext, 'include_dirs') and - not numpy_incl in ext.include_dirs): - ext.include_dirs.append(numpy_incl) - _build_ext.build_extensions(self) def generate_cython(): - + """Generate C files from Cython sources.""" from Cython.Build import cythonize - module_list = ["py_stringmatching/similarity_measure/cython/cython_affine.pyx", - "py_stringmatching/similarity_measure/cython/cython_jaro.pyx", - "py_stringmatching/similarity_measure/cython/cython_jaro_winkler.pyx", - "py_stringmatching/similarity_measure/cython/cython_levenshtein.pyx", - "py_stringmatching/similarity_measure/cython/cython_needleman_wunsch.pyx", - "py_stringmatching/similarity_measure/cython/cython_smith_waterman.pyx", - "py_stringmatching/similarity_measure/cython/cython_utils.pyx" - ] + module_list = [ + "py_stringmatching/similarity_measure/cython/cython_affine.pyx", + "py_stringmatching/similarity_measure/cython/cython_jaro.pyx", + "py_stringmatching/similarity_measure/cython/cython_jaro_winkler.pyx", + "py_stringmatching/similarity_measure/cython/cython_levenshtein.pyx", + "py_stringmatching/similarity_measure/cython/cython_needleman_wunsch.pyx", + "py_stringmatching/similarity_measure/cython/cython_smith_waterman.pyx", + "py_stringmatching/similarity_measure/cython/cython_utils.pyx" + ] + p = cythonize(module_list) - + if not p: raise RuntimeError("Running cythonize failed!") -cmdclass = {"build_ext": build_ext} - +def get_numpy_include(): + """Get numpy include directory using modern approach.""" + import numpy + return numpy.get_include() -if __name__ == "__main__": +def main(): + # Check if we need to generate Cython sources no_frills = (len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or sys.argv[1] in ('--help-commands', 'egg_info', '--version', 'clean'))) - + cwd = os.path.abspath(os.path.dirname(__file__)) if not os.path.exists(os.path.join(cwd, 'PKG-INFO')) and not no_frills: # Generate Cython sources, unless building from source release generate_cython() + + # Get numpy include directory + numpy_incl = get_numpy_include() + + # Define extensions + extensions = [ + Extension( + "py_stringmatching.similarity_measure.cython.cython_levenshtein", + ["py_stringmatching/similarity_measure/cython/cython_levenshtein.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_jaro", + ["py_stringmatching/similarity_measure/cython/cython_jaro.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_jaro_winkler", + ["py_stringmatching/similarity_measure/cython/cython_jaro_winkler.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_utils", + ["py_stringmatching/similarity_measure/cython/cython_utils.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_needleman_wunsch", + ["py_stringmatching/similarity_measure/cython/cython_needleman_wunsch.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_smith_waterman", + ["py_stringmatching/similarity_measure/cython/cython_smith_waterman.c"], + include_dirs=[numpy_incl] + ), + Extension( + "py_stringmatching.similarity_measure.cython.cython_affine", + ["py_stringmatching/similarity_measure/cython/cython_affine.c"], + include_dirs=[numpy_incl] + ) + ] + + setup(ext_modules=extensions) - # specify extensions that need to be compiled - extensions = [setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_levenshtein", - ["py_stringmatching/similarity_measure/cython/cython_levenshtein.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_jaro", - ["py_stringmatching/similarity_measure/cython/cython_jaro.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_jaro_winkler", - ["py_stringmatching/similarity_measure/cython/cython_jaro_winkler.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_utils", - ["py_stringmatching/similarity_measure/cython/cython_utils.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_needleman_wunsch", - ["py_stringmatching/similarity_measure/cython/cython_needleman_wunsch.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_smith_waterman", - ["py_stringmatching/similarity_measure/cython/cython_smith_waterman.c"], - include_dirs=[]), - setuptools.Extension("py_stringmatching.similarity_measure.cython.cython_affine", - ["py_stringmatching/similarity_measure/cython/cython_affine.c"], - include_dirs=[]) - - ] - - # find packages to be included. exclude benchmarks. - packages = setuptools.find_packages(exclude=["benchmarks", "benchmarks.custom_benchmarks"]) - - with open('README.rst') as f: - LONG_DESCRIPTION = f.read() - setuptools.setup( - name='py-stringmatching', - version='0.4.6', - description='Python library for string matching.', - long_description=LONG_DESCRIPTION, - url='https://sites.google.com/site/anhaidgroup/projects/magellan/py_stringmatching', - author='UW Magellan Team', - author_email='uwmagellan@gmail.com', - license='BSD', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Intended Audience :: Education', - 'License :: OSI Approved :: BSD License', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - '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', - 'Topic :: Scientific/Engineering', - 'Topic :: Utilities', - 'Topic :: Software Development :: Libraries', - ], - packages=packages, - install_requires=[ - 'numpy >= 1.7.0,<2.0', - ], - setup_requires=[ - 'numpy >= 1.7.0,<2.0' - ], - ext_modules=extensions, - cmdclass=cmdclass, - include_package_data=True, - zip_safe=False - ) +if __name__ == "__main__": + main() \ No newline at end of file