diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index dbd9e34..5b7ebce 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -2,35 +2,35 @@ name: Bug report description: Submit a bug report labels: ["bug"] body: - - type: textarea - attributes: - label: "Problem:" - description: > - Give a clear description on what's going wrong and how to reproduce it, if possible. + - type: textarea + attributes: + label: "Problem:" + description: > + Give a clear description on what's going wrong and how to reproduce it, if possible. - This should only be for *bugs*, not crashes. For that, use the crash template. + This should only be for *bugs*, not crashes. For that, use the crash template. - value: | - ```c - // Add your code here, if needed - ``` - validations: - required: true - - type: input - attributes: - label: "Version:" - value: | - What version(s) of PyAwaitable are you using? - validations: - required: true - - type: dropdown - attributes: - label: "Operating system(s) tested on:" - multiple: true - options: - - Linux - - macOS - - Windows - - Other - validations: - required: false + value: | + ```c + // Add your code here, if needed + ``` + validations: + required: true + - type: input + attributes: + label: "Version:" + value: | + What version(s) of PyAwaitable are you using? + validations: + required: true + - type: dropdown + attributes: + label: "Operating system(s) tested on:" + multiple: true + options: + - Linux + - macOS + - Windows + - Other + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml index 8e35b51..2aa088c 100644 --- a/.github/ISSUE_TEMPLATE/crash.yml +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -2,35 +2,35 @@ name: Crash report description: Submit a crash report labels: ["crash"] body: - - type: textarea - attributes: - label: "Crash Information:" - description: > - Give a clear description of what happened and how to reproduce it, if possible. If you aren't sure, attempt to run the program through a debugger, such as [Valgrind](https://valgrind.org/) or enabling [faulthandler](https://docs.python.org/3/library/faulthandler.html). + - type: textarea + attributes: + label: "Crash Information:" + description: > + Give a clear description of what happened and how to reproduce it, if possible. If you aren't sure, attempt to run the program through a debugger, such as [Valgrind](https://valgrind.org/) or enabling [faulthandler](https://docs.python.org/3/library/faulthandler.html). - This should only be for *crashes* not bugs. For that, use the bug template. + This should only be for *crashes* not bugs. For that, use the bug template. - value: | - ```c - // Add your code here, if needed - ``` - validations: - required: true - - type: input - attributes: - label: "Version:" - value: | - What version(s) of PyAwaitable are you using? - validations: - required: true - - type: dropdown - attributes: - label: "Operating system(s) tested on:" - multiple: true - options: - - Linux - - macOS - - Windows - - Other - validations: - required: false + value: | + ```c + // Add your code here, if needed + ``` + validations: + required: true + - type: input + attributes: + label: "Version:" + value: | + What version(s) of PyAwaitable are you using? + validations: + required: true + - type: dropdown + attributes: + label: "Operating system(s) tested on:" + multiple: true + options: + - Linux + - macOS + - Windows + - Other + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 4db4cf6..bd2b8e0 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -2,20 +2,20 @@ name: Feature description: Suggest a new feature. labels: ["feature"] body: - - type: markdown - attributes: - value: | - # Feature Proposal + - type: markdown + attributes: + value: | + # Feature Proposal - This is where you should propose a *new* feature to PyAwaitable. New features should not make breaking changes. This should be for something totally new, such as adding a new function to the ABI. General improvements to existing features should be made using an improvement request. - - type: textarea - attributes: - label: "Proposal:" - description: > - Outline your idea and why it would be a good idea for PyAwaitable. Make sure to include an example API for what this could look like if implemented. - value: | - ```c - // Example API - ``` - validations: - required: true + This is where you should propose a *new* feature to PyAwaitable. New features should not make breaking changes. This should be for something totally new, such as adding a new function to the ABI. General improvements to existing features should be made using an improvement request. + - type: textarea + attributes: + label: "Proposal:" + description: > + Outline your idea and why it would be a good idea for PyAwaitable. Make sure to include an example API for what this could look like if implemented. + value: | + ```c + // Example API + ``` + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/improvement.yml b/.github/ISSUE_TEMPLATE/improvement.yml index 10c7f5a..3583f43 100644 --- a/.github/ISSUE_TEMPLATE/improvement.yml +++ b/.github/ISSUE_TEMPLATE/improvement.yml @@ -2,20 +2,20 @@ name: Improvement description: Suggest an improvement to an existing feature. labels: ["improvement"] body: - - type: markdown - attributes: - value: | - # Improvement + - type: markdown + attributes: + value: | + # Improvement - An improvement proposal should be related to an *existing* feature. For example, adding some functionality to the *existing* coroutine implementation. - - type: textarea - attributes: - label: "Description:" - description: > - Outline what needs to be improved and why? Be sure to include an example API if necessary. - value: | - ```c - // Example API - ``` - validations: - required: true + An improvement proposal should be related to an *existing* feature. For example, adding some functionality to the *existing* coroutine implementation. + - type: textarea + attributes: + label: "Description:" + description: > + Outline what needs to be improved and why? Be sure to include an example API if necessary. + value: | + ```c + // Example API + ``` + validations: + required: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a01c0a5..cfcd9c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,59 +1,59 @@ name: Build on: - push: - tags: - - v* - branches: - - master - paths: - - 'src/**' - pull_request: - branches: - - master + push: + tags: + - v* + branches: + - master + paths: + - "src/**" + pull_request: + branches: + - master concurrency: - group: build-${{ github.head_ref }} - cancel-in-progress: true + group: build-${{ github.head_ref }} + cancel-in-progress: true jobs: - pure-python-wheel-and-sdist: - name: Build a pure Python wheel and source distribution - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install build dependencies - run: python -m pip install --upgrade build - - - name: Build - run: python -m build - - - uses: actions/upload-artifact@v4 - with: - name: artifacts - path: dist/* - if-no-files-found: error - - publish: - name: Publish release - needs: - - pure-python-wheel-and-sdist - runs-on: ubuntu-latest - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') - - steps: - - uses: actions/download-artifact@v4 - with: - name: artifacts - path: dist - - - name: Push build artifacts to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - skip_existing: true - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + pure-python-wheel-and-sdist: + name: Build a pure Python wheel and source distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install build dependencies + run: python -m pip install --upgrade build + + - name: Build + run: python -m build + + - uses: actions/upload-artifact@v4 + with: + name: artifacts + path: dist/* + if-no-files-found: error + + publish: + name: Publish release + needs: + - pure-python-wheel-and-sdist + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + + steps: + - uses: actions/download-artifact@v4 + with: + name: artifacts + path: dist + + - name: Push build artifacts to PyPI + uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + skip_existing: true + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9238f44..327e30f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,73 +1,81 @@ name: Tests on: - push: - branches: - - master - pull_request: - branches: - - master + push: + branches: + - master + pull_request: + branches: + - master concurrency: - group: test-${{ github.head_ref }} - cancel-in-progress: true + group: test-${{ github.head_ref }} + cancel-in-progress: true env: - PYTHONUNBUFFERED: "1" - FORCE_COLOR: "1" - PYTHONIOENCODING: "utf8" + PYTHONUNBUFFERED: "1" + FORCE_COLOR: "1" + PYTHONIOENCODING: "utf8" jobs: - changes: - name: Check for changed files - runs-on: ubuntu-latest - outputs: - source: ${{ steps.filter.outputs.source }} - csource: ${{ steps.filter.outputs.csource }} - steps: - - uses: actions/checkout@v2 - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - source: - - 'src/**' + changes: + name: Check for changed files + runs-on: ubuntu-latest + outputs: + source: ${{ steps.filter.outputs.source }} + tests: ${{ steps.filter.outputs.tests }} + steps: + - uses: actions/checkout@v2 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + source: + - 'src/**' + tests: + - 'tests/**' - run-tests: - needs: changes - if: ${{ needs.changes.outputs.source == 'true' }} - name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: true - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - steps: - - uses: actions/checkout@v3 + run-tests: + needs: changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.tests }} + name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Hatch - uses: pypa/hatch@install + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} - - name: Run tests - run: hatch test + - name: Install Hatch + uses: pypa/hatch@install - tests-pass: - runs-on: ubuntu-latest - name: All tests passed - if: always() + - name: Run tests + run: hatch test - needs: - - run-tests + - name: Run build test for scikit-build-core + run: hatch run test-build:scikit-build-core - steps: - - name: Check whether all tests passed - uses: re-actors/alls-green@release/v1 - with: - jobs: ${{ toJSON(needs) }} - allowed-skips: ${{ toJSON(needs) }} + - name: Run build test for meson-python + run: hatch run test-build:meson + + tests-pass: + runs-on: ubuntu-latest + name: All tests passed + if: always() + + needs: + - run-tests + + steps: + - name: Check whether all tests passed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + allowed-skips: ${{ toJSON(needs) }} diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 370af4a..1fdc7b6 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -1,67 +1,67 @@ name: Triage on: - pull_request: - types: - - "opened" - - "reopened" - - "synchronize" - - "labeled" - - "unlabeled" + pull_request: + types: + - "opened" + - "reopened" + - "synchronize" + - "labeled" + - "unlabeled" jobs: - changelog_check: - runs-on: ubuntu-latest - name: Check for changelog updates - steps: - - name: "Check if the source directory was changed" - uses: dorny/paths-filter@v3 - id: changes - with: - filters: | - src: - - 'src/**' + changelog_check: + runs-on: ubuntu-latest + name: Check for changelog updates + steps: + - name: "Check if the source directory was changed" + uses: dorny/paths-filter@v3 + id: changes + with: + filters: | + src: + - 'src/**' - - name: "Check for changelog updates" - if: steps.changes.outputs.src == 'true' - uses: brettcannon/check-for-changed-files@v1 - with: - file-pattern: | - CHANGELOG.md - skip-label: "skip changelog" - failure-message: "Missing a CHANGELOG.md update; please add one or apply the ${skip-label} label to the pull request" + - name: "Check for changelog updates" + if: steps.changes.outputs.src == 'true' + uses: brettcannon/check-for-changed-files@v1 + with: + file-pattern: | + CHANGELOG.md + skip-label: "skip changelog" + failure-message: "Missing a CHANGELOG.md update; please add one or apply the ${skip-label} label to the pull request" - tests_check: - runs-on: ubuntu-latest - name: Check for updated tests - steps: - - name: "Check if the source directory was changed" - uses: dorny/paths-filter@v3 - id: changes - with: - filters: | - src: - - 'src/**' + tests_check: + runs-on: ubuntu-latest + name: Check for updated tests + steps: + - name: "Check if the source directory was changed" + uses: dorny/paths-filter@v3 + id: changes + with: + filters: | + src: + - 'src/**' - - name: "Check for test updates" - if: steps.changes.outputs.src == 'true' - uses: brettcannon/check-for-changed-files@v1 - with: - file-pattern: | - tests/* - skip-label: "skip tests" - failure-message: "Missing unit tests; please add some or apply the ${skip-label} label to the pull request" + - name: "Check for test updates" + if: steps.changes.outputs.src == 'true' + uses: brettcannon/check-for-changed-files@v1 + with: + file-pattern: | + tests/* + skip-label: "skip tests" + failure-message: "Missing unit tests; please add some or apply the ${skip-label} label to the pull request" - all_green: - runs-on: ubuntu-latest - name: PR has no missing information - if: always() + all_green: + runs-on: ubuntu-latest + name: PR has no missing information + if: always() - needs: - - changelog_check - - tests_check + needs: + - changelog_check + - tests_check - steps: - - name: Check whether jobs passed - uses: re-actors/alls-green@release/v1 - with: - jobs: ${{ toJSON(needs) }} + steps: + - name: Check whether jobs passed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/README.md b/README.md index 988ce57..5cc8c4b 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ [![Build](https://github.com/ZeroIntensity/pyawaitable/actions/workflows/build.yml/badge.svg)](https://github.com/ZeroIntensity/pyawaitable/actions/workflows/build.yml) ![Tests](https://github.com/ZeroIntensity/pyawaitable/actions/workflows/tests.yml/badge.svg) -- [Docs](https://awaitable.zintensity.dev) +- [Docs](https://pyawaitable.zintensity.dev) - [Source](https://github.com/ZeroIntensity/pyawaitable) - [PyPI](https://pypi.org/project/pyawaitable) ## What is it? -PyAwaitable is the *only* library to support writing and calling asynchronous Python functions from pure C code (with the exception of manually implementing an awaitable class from scratch, which is essentially what PyAwaitable does). +PyAwaitable is the _only_ library to support writing and calling asynchronous Python functions from pure C code (with the exception of manually implementing an awaitable class from scratch, which is essentially what PyAwaitable does). It was originally designed to be directly part of CPython--you can read the [scrapped PEP](https://gist.github.com/ZeroIntensity/8d32e94b243529c7e1c27349e972d926) about it. Since this library only uses the public ABI, it's better fit outside of CPython, as a library. diff --git a/docs/installation.md b/docs/installation.md index 520b22e..89974c5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -91,7 +91,7 @@ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) Python_add_library(_module MODULE src/module.c WITH_SOABI) target_include_directories(_module PRIVATE $ENV{PYAWAITABLE_INCLUDE}) -install(TARGETS _module DESTINATION ${SKBUILD_PROJECT_NAME}) +install(TARGETS _module DESTINATION .) ``` ### `meson-python` @@ -104,6 +104,7 @@ py = import('python').find_installation(pure: false) pyawaitable_include = run_command('pyawaitable --include', check: true).stdout().strip() py.extension_module( + '_module', 'src/module.c', install: true, include_directories: [pyawaitable_include], @@ -122,18 +123,6 @@ module_exec(PyObject *mod) return PyAwaitable_Init(); } -static PyModuleDef_Slot module_slots[] = { - {Py_mod_exec, module_exec}, - {0, NULL} -}; - -static PyModuleDef module = { - .m_base = PyModuleDef_HEAD_INIT, - .m_size = 0, - .m_slots = module_slots, - .m_methods = module_methods -}; - /* Equivalent to the following Python function: @@ -157,10 +146,22 @@ async_function(PyObject *self, PyObject *coro) return awaitable; } +static PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL} +}; + static PyMethodDef module_methods[] = { {"async_function", async_function, METH_O, NULL}, {NULL, NULL, 0, NULL}, -} +}; + +static PyModuleDef module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_size = 0, + .m_slots = module_slots, + .m_methods = module_methods +}; PyMODINIT_FUNC PyInit__module() diff --git a/hatch.toml b/hatch.toml index a29d0e8..592ca52 100644 --- a/hatch.toml +++ b/hatch.toml @@ -26,3 +26,13 @@ default-args = ["--verbose"] [[envs.hatch-test.matrix]] python = ["3.13", "3.12", "3.11", "3.10", "3.9"] + +[envs.test-build] +dependencies = [ + "pyawaitable_test_meson @ {root:uri}/tests/builds/meson", + "pyawaitable_test_sbc @ {root:uri}/tests/builds/scikit-build-core", +] + +[envs.test-build.scripts] +meson = "python3 tests/builds/ensure_build_worked.py _meson_module" +scikit-build-core = "python3 tests/builds/ensure_build_worked.py _sbc_module" diff --git a/hatch_build.py b/hatch_build.py index 7f5a186..3670eb5 100644 --- a/hatch_build.py +++ b/hatch_build.py @@ -71,7 +71,7 @@ class BuildHookInterface: /* * PyAwaitable - Autogenerated distribution copy of version {version} * - * Docs: https://awaitable.zintensity.dev + * Docs: https://pyawaitable.zintensity.dev * Source: https://github.com/ZeroIntensity/pyawaitable */ """ diff --git a/mkdocs.yml b/mkdocs.yml index 9ab11c0..092e667 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: PyAwaitable -site_url: https://awaitable.zintensity.dev +site_url: https://pyawaitable.zintensity.dev repo_url: https://github.com/ZeroIntensity/pyawaitable repo_name: ZeroIntensity/pyawaitable diff --git a/pyproject.toml b/pyproject.toml index d15560e..ad37d80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dynamic = ["version"] requires-python = ">=3.9" [project.urls] -Documentation = "https://awaitable.zintensity.dev" +Documentation = "https://pyawaitable.zintensity.dev" Issues = "https://github.com/ZeroIntensity/pyawaitable/issues" Source = "https://github.com/ZeroIntensity/pyawaitable" diff --git a/src/pyawaitable/__init__.py b/src/pyawaitable/__init__.py index 2967dd5..b8403ed 100644 --- a/src/pyawaitable/__init__.py +++ b/src/pyawaitable/__init__.py @@ -4,7 +4,7 @@ It's unlikely that you want to import this module from Python, other than for use in setuptools. -Docs: https://awaitable.zintensity.dev/ +Docs: https://pyawaitable.zintensity.dev/ Source: https://github.com/ZeroIntensity/pyawaitable """ diff --git a/src/pyawaitable/__main__.py b/src/pyawaitable/__main__.py index c15f86f..9a8dd07 100644 --- a/src/pyawaitable/__main__.py +++ b/src/pyawaitable/__main__.py @@ -3,7 +3,7 @@ from pyawaitable import __version__, include as get_include DEFAULT_MESSAGE = f"""PyAwaitable {__version__} -Documentation: https://awaitable.zintensity.dev +Documentation: https://pyawaitable.zintensity.dev Source: https://github.com/ZeroIntensity/pyawaitable""" def option_main(include: bool, version: bool) -> None: diff --git a/tests/builds/ensure_build_worked.py b/tests/builds/ensure_build_worked.py new file mode 100644 index 0000000..68d657e --- /dev/null +++ b/tests/builds/ensure_build_worked.py @@ -0,0 +1,79 @@ +""" +Script to ensure that an extension module containing PyAwaitable was successfully built. +Intended to be run by Hatch in CI. +""" +import optparse +import sys +import asyncio +import traceback +import importlib +import site +import os +from typing import TypeVar + +WINDOWS_HATCH_BUG = """PyAwaitable couldn't be imported. +This is probably a bug in Hatch environments, so this is being skipped +on Windows.""" + +try: + import pyawaitable +except ImportError: + if os.name != 'nt': + raise + + print(WINDOWS_HATCH_BUG) + sys.exit(0) + +T = TypeVar("T") + +def not_none(value: T | None) -> T: + assert value is not None, "got None where None shouldn't be" + return value + +def debug_directory(what: str, path: str) -> None: + print(f"{what}: {path}", file=sys.stderr) + print(f" Contents of {what}:", file=sys.stderr) + for root, _, files in os.walk(path): + for file in files: + print(f" {what}: {os.path.join(root, file)}", file=sys.stderr) + +def debug_import_error(error: ImportError) -> None: + debug_directory("PyAwaitable Include", pyawaitable.include()) + debug_directory("User Site", not_none(site.USER_SITE)) + debug_directory("User Base", not_none(site.USER_BASE)) + print(error) + +def main(): + parser = optparse.OptionParser() + _, args = parser.parse_args() + + if not args: + parser.error("Expected one argument.") + + mod = args[0] + + called = False + async def dummy(): + await asyncio.sleep(0) + nonlocal called + called = True + + try: + asyncio.run(importlib.import_module(mod).async_function(dummy())) + except BaseException as err: + traceback.print_exc() + if isinstance(err, ImportError): + debug_import_error(err) + print("Failed to import the module!", file=sys.stderr) + else: + print("Build failed!", file=sys.stderr) + sys.exit(1) + + if not called: + print("Build doesn't work!", file=sys.stderr) + sys.exit(1) + + print("Success!") + +if __name__ == "__main__": + main() diff --git a/tests/builds/meson/meson.build b/tests/builds/meson/meson.build new file mode 100644 index 0000000..8e88ae2 --- /dev/null +++ b/tests/builds/meson/meson.build @@ -0,0 +1,12 @@ +project('_meson_module', 'c') + +py = import('python').find_installation(pure: false) +pyawaitable_include = run_command('pyawaitable', '--include', check: true).stdout().strip() + + +py.extension_module( + '_meson_module', + 'module.c', + install: true, + include_directories: [pyawaitable_include], +) \ No newline at end of file diff --git a/tests/builds/meson/module.c b/tests/builds/meson/module.c new file mode 100644 index 0000000..713abed --- /dev/null +++ b/tests/builds/meson/module.c @@ -0,0 +1,54 @@ +#include +#include + +static int +module_exec(PyObject *mod) +{ + return PyAwaitable_Init(); +} + +/* + Equivalent to the following Python function: + + async def async_function(coro: collections.abc.Awaitable) -> None: + await coro + + */ +static PyObject * +async_function(PyObject *self, PyObject *coro) +{ + PyObject *awaitable = PyAwaitable_New(); + if (awaitable == NULL) { + return NULL; + } + + if (PyAwaitable_AddAwait(awaitable, coro, NULL, NULL) < 0) { + Py_DECREF(awaitable); + return NULL; + } + + return awaitable; +} + +static PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL} +}; + +static PyMethodDef module_methods[] = { + {"async_function", async_function, METH_O, NULL}, + {NULL, NULL, 0, NULL}, +}; + +static PyModuleDef module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_size = 0, + .m_slots = module_slots, + .m_methods = module_methods +}; + +PyMODINIT_FUNC +PyInit__meson_module() +{ + return PyModuleDef_Init(&module); +} \ No newline at end of file diff --git a/tests/builds/meson/pyproject.toml b/tests/builds/meson/pyproject.toml new file mode 100644 index 0000000..c4717b5 --- /dev/null +++ b/tests/builds/meson/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["meson-python"] +build-backend = 'mesonpy' + +[project] +name = "pyawaitable_test_meson" +version = "0.0.0" diff --git a/tests/builds/scikit-build-core/CMakeLists.txt b/tests/builds/scikit-build-core/CMakeLists.txt new file mode 100644 index 0000000..524eebc --- /dev/null +++ b/tests/builds/scikit-build-core/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.15...3.30) +project(${SKBUILD_PROJECT_NAME} LANGUAGES C) + +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) + +Python_add_library(_sbc_module MODULE module.c WITH_SOABI) +target_include_directories(_sbc_module PRIVATE $ENV{PYAWAITABLE_INCLUDE}) +install(TARGETS _sbc_module DESTINATION .) diff --git a/tests/builds/scikit-build-core/module.c b/tests/builds/scikit-build-core/module.c new file mode 100644 index 0000000..7c67f0f --- /dev/null +++ b/tests/builds/scikit-build-core/module.c @@ -0,0 +1,54 @@ +#include +#include + +static int +module_exec(PyObject *mod) +{ + return PyAwaitable_Init(); +} + +/* + Equivalent to the following Python function: + + async def async_function(coro: collections.abc.Awaitable) -> None: + await coro + + */ +static PyObject * +async_function(PyObject *self, PyObject *coro) +{ + PyObject *awaitable = PyAwaitable_New(); + if (awaitable == NULL) { + return NULL; + } + + if (PyAwaitable_AddAwait(awaitable, coro, NULL, NULL) < 0) { + Py_DECREF(awaitable); + return NULL; + } + + return awaitable; +} + +static PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL} +}; + +static PyMethodDef module_methods[] = { + {"async_function", async_function, METH_O, NULL}, + {NULL, NULL, 0, NULL}, +}; + +static PyModuleDef module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_size = 0, + .m_slots = module_slots, + .m_methods = module_methods +}; + +PyMODINIT_FUNC +PyInit__sbc_module() +{ + return PyModuleDef_Init(&module); +} \ No newline at end of file diff --git a/tests/builds/scikit-build-core/pyproject.toml b/tests/builds/scikit-build-core/pyproject.toml new file mode 100644 index 0000000..92f07d9 --- /dev/null +++ b/tests/builds/scikit-build-core/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["scikit-build-core"] +build-backend = "scikit_build_core.build" + +[project] +name = "pyawaitable_test_sbc" +version = "0.0.0"