diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb2be7d2..c75047b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,9 @@ jobs: - uses: actions/checkout@master - uses: actions/setup-python@master with: - python-version: 3.13 - - run: pip install -r requirements-build-3_13.txt - - run: python setup.py sdist + python-version: 3.14 + - run: python -m pip install build + - run: python -m build --sdist - uses: actions/upload-artifact@v4 with: name: dist-sdist @@ -48,11 +48,12 @@ jobs: matrix: os: ${{ fromJson(needs.matrix_config.outputs.matrix_os) }} python: - - {minor: 10, req_build: 'requirements-build-3_11.txt', req_test: 'requirements-dev-3_11.txt', ft: '0'} - - {minor: 11, req_build: 'requirements-build-3_11.txt', req_test: 'requirements-dev-3_11.txt', ft: '0'} - - {minor: 12, req_build: 'requirements-build-3_12.txt', req_test: 'requirements-dev-3_12.txt', ft: '0'} - - {minor: 13, req_build: 'requirements-build-3_13.txt', req_test: 'requirements-dev-3_13.txt', ft: '0'} - - {minor: 13t, req_build: 'requirements-build-3_13.txt', req_test: 'requirements-dev-3_13.txt', ft: '1'} + - {minor: 10, req_test: 'requirements-dev-3_11.txt', ft: '0'} + - {minor: 11, req_test: 'requirements-dev-3_11.txt', ft: '0'} + - {minor: 12, req_test: 'requirements-dev-3_12.txt', ft: '0'} + - {minor: 13, req_test: 'requirements-dev-3_13.txt', ft: '0'} + - {minor: 14, req_test: 'requirements-dev-3_14.txt', ft: '0'} + - {minor: 14t, req_test: 'requirements-dev-3_14.txt', ft: '1'} runs-on: ${{ matrix.os }} outputs: @@ -65,7 +66,7 @@ jobs: - run: echo '::add-matcher::.github/problem-matchers/msvc.json' if: startsWith(matrix.os, 'windows-') - - uses: pypa/cibuildwheel@v2.23.3 + - uses: pypa/cibuildwheel@v3.2.0 if: matrix.os != 'macos-13-xlarge' with: output-dir: dist @@ -73,20 +74,18 @@ jobs: CIBW_BUILD: cp3${{ matrix.python.minor }}-* CIBW_ARCHS_WINDOWS: x86 AMD64 CIBW_ARCHS_MACOS: x86_64 - CIBW_BEFORE_BUILD: pip install -r {project}/${{ matrix.python.req_build }} CIBW_BEFORE_TEST: pip install -r {project}/${{ matrix.python.req_test }} CIBW_TEST_COMMAND: pytest {project}/test CIBW_ENABLE: ${{ matrix.python.ft == '1' && 'cpython-freethreading' || '' }} - run: pip install pipx if: matrix.os == 'macos-13-xlarge' - - uses: pypa/cibuildwheel@v2.23.3 + - uses: pypa/cibuildwheel@v3.2.0 if: matrix.os == 'macos-13-xlarge' with: output-dir: dist env: CIBW_BUILD: cp3${{ matrix.python.minor }}-macosx_arm64 - CIBW_BEFORE_BUILD: pip install -r {project}/${{ matrix.python.req_build }} CIBW_BEFORE_TEST: pip install -r {project}/${{ matrix.python.req_test }} CIBW_TEST_COMMAND: pytest {project}/test CIBW_ENABLE: ${{ matrix.python.ft == '1' && 'cpython-freethreading' || '' }} diff --git a/MANIFEST.in b/MANIFEST.in index 84e9b5a2..cb6cad3a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include src/__init__.pyi src/py.typed include src/*.h +include VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..cb7e5f6f --- /dev/null +++ b/VERSION @@ -0,0 +1,2 @@ +1.2.0 + diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..193e6d8f --- /dev/null +++ b/noxfile.py @@ -0,0 +1,93 @@ +import nox +import sys + +ARTIFACTS = ( + "*.egg-info", + ".hypothesis", + "build", + "dist", + "src/*.so", +) + +# Make `nox` default to running tests if you just do `nox` +nox.options.sessions = ["test"] + + +def do_clean(session: nox.Session) -> None: + # uninstall arraykit + session.run( + sys.executable, "-m", "pip", + "--disable-pip-version-check", "uninstall", "--yes", "arraykit", + external=True + ) + # remove artifacts + for artifact in sorted(ARTIFACTS): + session.run("rm", "-rf", artifact, external=True) + +def do_build(session: nox.Session) -> None: + # keep -v to see warnings; no build isolation to match your invoke cmd + session.run( + sys.executable, "-m", "pip", + "--disable-pip-version-check", + "install", "-v", "--no-build-isolation", ".", + external=True + ) + +def do_test(session: nox.Session) -> None: + session.run( + "pytest", + "-s", + "--disable-pytest-warnings", + "--tb=native", + external=True, + ) + +def do_performance(session: nox.Session) -> None: + """Run performance benchmarks.""" + args = [sys.executable, "-m", "performance"] + + if session.posargs: + args.extend(["--names"] + session.posargs) + + session.run(*args, external=True) + +def do_lint(session: nox.Session) -> None: + session.run( + "pylint", + "-f", "colorized", + "*.py", "performance", "src", "test", + external=True, + ) + + +# NOTE: use `nox -s build` to launch a session + +@nox.session(python=False) # use current environment +def clean(session): + """Clean build artifacts and uninstall arraykit.""" + do_clean(session) + +@nox.session(python=False) +def build(session): + """Clean then build/install locally (like invoke: build depends on clean).""" + do_clean(session) + do_build(session) + +@nox.session(python=False) +def test(session): + """Build then run pytest (like invoke: test depends on build).""" + do_clean(session) + do_build(session) + do_test(session) + +@nox.session(python=False) +def performance(session): + """Build then run performance benches (like invoke: performance depends on build).""" + do_clean(session) + do_build(session) + do_performance(session) + +@nox.session(python=False) +def lint(session): + """Run pylint static analysis.""" + do_lint(session) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..21222e84 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,56 @@ +[build-system] +requires = [ + "setuptools>=64", + "wheel", + "numpy>=2.2" +] +build-backend = "setuptools.build_meta" + +[project] +name = "arraykit" +description = "Array utilities for StaticFrame" +readme = { file = "README.rst", content-type = "text/x-rst" } +requires-python = ">=3.10" + +dynamic = ["version"] + +authors = [ + { name = "Christopher Ariza" }, + { name = "Brandt Bucher" }, + { name = "Charles Burkland" }, +] +license = "MIT" +keywords = ["numpy", "array"] +dependencies = [ + "numpy>=1.24.3", +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Software Development", + "Programming Language :: C", + "Programming Language :: Python :: Implementation :: CPython", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Free Threading", + "Typing :: Typed", +] + +[project.urls] +Homepage = "https://github.com/static-frame/arraykit" + +[tool.setuptools] +package-dir = { "arraykit" = "src" } + +[tool.setuptools.package-data] +arraykit = ["__init__.pyi", "py.typed"] + +[tool.setuptools.dynamic] +version = { file = "VERSION" } + diff --git a/requirements-build-3_11.txt b/requirements-build-3_11.txt deleted file mode 100644 index 86438d81..00000000 --- a/requirements-build-3_11.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy==2.0.2 - diff --git a/requirements-build-3_12.txt b/requirements-build-3_12.txt deleted file mode 100644 index fe9ad60c..00000000 --- a/requirements-build-3_12.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy==2.2.6 -setuptools==80.* diff --git a/requirements-build-3_13.txt b/requirements-build-3_13.txt deleted file mode 100644 index 467a95b6..00000000 --- a/requirements-build-3_13.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy==2.3.1 -setuptools==80.* diff --git a/requirements-dev-3_11.txt b/requirements-dev-3_11.txt index d34c9454..20e83c99 100644 --- a/requirements-dev-3_11.txt +++ b/requirements-dev-3_11.txt @@ -1,4 +1,4 @@ numpy==2.0.2 pytest==7.1.2 -invoke==2.2.0 +nox==2025.5.1 hypothesis==6.10.1 diff --git a/requirements-dev-3_12.txt b/requirements-dev-3_12.txt index e692ee9a..9c8e590d 100644 --- a/requirements-dev-3_12.txt +++ b/requirements-dev-3_12.txt @@ -1,4 +1,4 @@ numpy==2.2.6 pytest==7.1.2 -invoke==2.2.0 +nox==2025.5.1 hypothesis==6.10.1 diff --git a/requirements-dev-3_13.txt b/requirements-dev-3_13.txt index e029979c..11ec293f 100644 --- a/requirements-dev-3_13.txt +++ b/requirements-dev-3_13.txt @@ -1,4 +1,4 @@ numpy==2.3.1 pytest==8.3.3 -invoke==2.2.0 +nox==2025.5.1 hypothesis==6.131.16 diff --git a/requirements-dev-3_14.txt b/requirements-dev-3_14.txt new file mode 100644 index 00000000..542eb9a2 --- /dev/null +++ b/requirements-dev-3_14.txt @@ -0,0 +1,4 @@ +numpy==2.3.3 +pytest==8.3.3 +nox==2025.5.1 +hypothesis==6.131.16 diff --git a/setup.py b/setup.py index 103a1658..20e4da61 100644 --- a/setup.py +++ b/setup.py @@ -1,35 +1,9 @@ -import site -import os -from os import path -import typing as tp -from setuptools import Extension # type: ignore -from setuptools import setup +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext +import site, os +from pathlib import Path -AK_VERSION = '1.1.0' - -ROOT_DIR_FP = path.abspath(path.dirname(__file__)) - -def get_long_description() -> str: - with open(path.join(ROOT_DIR_FP, 'README.rst'), encoding='utf-8') as f: - msg = [] - collect = False - start = -1 - for i, line in enumerate(f): - if line.startswith('arraykit'): - start = i + 2 # skip this line and the next - if i == start: - collect = True - if collect: - msg.append(line) - return ''.join(msg).strip() - - -# NOTE: we do this to avoid importing numpy: https://stackoverflow.com/questions/54117786/add-numpy-get-include-argument-to-setuptools-without-preinstalled-numpy -# we used to import the following to get directories: -# from numpy.distutils.misc_util import get_info -# import numpy as np # type: ignore -# get_info('npymath')['library_dirs'] -# get_info('npymath')['libraries'] +AK_VERSION = Path("VERSION").read_text(encoding="utf-8").strip() def get_ext_dir(*components: tp.Iterable[str]) -> tp.Sequence[str]: dirs = [] @@ -39,57 +13,26 @@ def get_ext_dir(*components: tp.Iterable[str]) -> tp.Sequence[str]: dirs.append(fp) return dirs -ak_extension = Extension( - name='arraykit._arraykit', # build into module +ext_modules = [ + Extension( + name="arraykit._arraykit", sources=[ - 'src/_arraykit.c', - 'src/array_go.c', - 'src/array_to_tuple.c', - 'src/block_index.c', - 'src/delimited_to_arrays.c', - 'src/methods.c', - 'src/tri_map.c', - 'src/auto_map.c', + "src/_arraykit.c", + "src/array_go.c", + "src/array_to_tuple.c", + "src/block_index.c", + "src/delimited_to_arrays.c", + "src/methods.c", + "src/tri_map.c", + "src/auto_map.c", ], + include_dirs=get_ext_dir('numpy', '_core', 'include') + ['src'], library_dirs=get_ext_dir('numpy', '_core', 'lib'), define_macros=[("AK_VERSION", AK_VERSION)], - libraries=['npymath'], # not including mlib at this time - ) + libraries=["npymath"], + ) +] + +setup(ext_modules=ext_modules) -setup( - name='arraykit', - version=AK_VERSION, - description='Array utilities for StaticFrame', - long_description=get_long_description(), - long_description_content_type='text/x-rst', # use text/x-rst - python_requires='>=3.10', - install_requires=['numpy>=1.24.3'], - url='https://github.com/static-frame/arraykit', - author='Christopher Ariza, Brandt Bucher, Charles Burkland', - license='MIT', - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Topic :: Software Development', - "Programming Language :: C", - "Programming Language :: Python :: Implementation :: CPython", - 'License :: OSI Approved :: MIT License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', - 'Programming Language :: Python :: Free Threading', - 'Typing :: Typed', - ], - keywords='numpy array', - packages=['arraykit'], - package_dir={'arraykit': 'src'}, - package_data={'arraykit': ['__init__.pyi', 'py.typed']}, - include_package_data=True, - ext_modules=[ak_extension], -) diff --git a/tasks.py b/tasks.py deleted file mode 100644 index 6b95cc69..00000000 --- a/tasks.py +++ /dev/null @@ -1,49 +0,0 @@ -import sys - -import invoke - -ARTIFACTS = ( - '*.egg-info', - '.hypothesis', - 'build', - 'dist', - 'src/*.so' -) - - -@invoke.task -def clean(context): - '''Clean doc and build artifacts - ''' - cmd = f'{sys.executable} -m pip --disable-pip-version-check uninstall --yes arraykit' - context.run(cmd, echo=True, pty=True) - - for artifact in sorted(ARTIFACTS): - context.run(f'rm -rf {artifact}', echo=True, pty=True) - - -@invoke.task(clean) -def build(context): - # context.run('pip install -r requirements-test.txt', echo=True, pty=True) - # keep verbose to see warnings - context.run(f'{sys.executable} -m pip --disable-pip-version-check -v install --no-build-isolation .', echo=True, pty=True) - - -@invoke.task(build) -def test(context): - cmd = 'pytest -s --disable-pytest-warnings --tb=native' - context.run(cmd, echo=True, pty=True) - - -@invoke.task(build) -def performance(context, names=''): - context.run(f'{sys.executable} -m performance {"--names" if names else ""} {names}', echo=True, pty=True) - - -@invoke.task -def lint(context): - '''Run pylint static analysis. - ''' - cmd = 'pylint -f colorized *.py performance src test' - context.run(cmd, echo=True, pty=True) - diff --git a/test/test_pyi.py b/test/test_pyi.py index 86fc76a8..8f2bcd31 100644 --- a/test/test_pyi.py +++ b/test/test_pyi.py @@ -14,6 +14,8 @@ class Interface(tp.NamedTuple): @staticmethod def _valid_name(name: str) -> bool: + if name in ('__annotate__', '__class__', '__annotate_func__'): + return False if name.startswith('__'): return True if name.startswith('_'):