From 8d00fbd597e25968b00d8688ce89ef405f2e0155 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 25 Jul 2024 13:18:57 +0200 Subject: [PATCH 01/13] Started migration to pyproject.toml, working on Windows --- .flake8 | 2 ++ MANIFEST.in | 3 -- PKG-INFO | 12 ------- pyproject.toml | 83 ++++++++++++++++++++++++++++++++++++++++++------ setup.cfg | 15 --------- setup.py | 85 -------------------------------------------------- tox.ini | 17 ---------- 7 files changed, 75 insertions(+), 142 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 PKG-INFO delete mode 100644 setup.cfg diff --git a/.flake8 b/.flake8 index 2bcd70e3..816b90a4 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,4 @@ [flake8] max-line-length = 88 +ignore = F401, W503 +# flake8 cannot be configured through pyproject.toml diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 90891779..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include adslib/* -include LICENSE -include README.rst diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index 19b85975..00000000 --- a/PKG-INFO +++ /dev/null @@ -1,12 +0,0 @@ -Metadata-Version: 1.1 -Name: adsPy -Version: 1.0.2 -Summary: Python wrapper for TwinCAT ADS-DLL -Home-page: http://mrleeh.square7.ch/ -Author: Stefan Lehmann -Author-email: mrleeh@gmx.de -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN -Requires: ctypes -Provides: adsPy diff --git a/pyproject.toml b/pyproject.toml index 0da0448f..cb9aa984 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,72 @@ +[project] +name = "pyads" +description = "Python wrapper for TwinCAT ADS library" +authors = [ + { name = "Stefan Lehmann", email = "Stefan.St.Lehmann@gmail.com" }, +] +readme = "README.md" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Libraries", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", +] +license = { text = "MIT" } +dynamic = ["version"] + +[project.urls] +Repository = "https://github.com/MrLeeh/pyads" +Documentation = "https://pyads.readthedocs.io" + +[build-system] +requires = ["setuptools >= 61.0", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["pyads", "pyads.testserver"] + +[tool.setuptools.package-data] +pyads = [ + "adslib.so", + "adslib/" +] + +[tool.setuptools.dynamic] +version = {attr = "pyads.__version__"} + +[tool.tox] +legacy_tox_ini = """ + [tox] + envlist = py37, py38, py39, py310 + + [testenv] + commands = discover + deps = discover + changedir = tests + whitelist_externals=* + passenv = TWINCAT3DIR + + [pytest] + testpaths = tests +""" + [tool.black] line-length = 88 -target-version = ['py37', 'py38', 'py39', 'py310'] -include = '\.pyi?$' -exclude = ''' - +target-version = ["py37", "py38", "py39", "py310"] +include = ".pyi?$" +exclude = """ ( /( - \.eggs - | \.git - | \.mypy_cache - | \.tox - | \.venv + .eggs + | .git + | .mypy_cache + | .tox + | .venv | _build | buck-out | build @@ -20,4 +76,11 @@ exclude = ''' )/ | pyads/__init__.py ) -''' \ No newline at end of file +""" + +[tool.pydocstyle] +ignore = ["D105", "D213", "D203", "D107"] + +[tool.coverage.run] +include = ["pyads/*"] +omit = ["pyads/testserver/__main__.py"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 929d9e1e..00000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ - -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[coverage:run] -include = pyads/* -omit = - pyads/testserver/__main__.py - -[flake8] -ignore = F401, W503 - -[pydocstyle] -ignore = D105, D213, D203, D107 diff --git a/setup.py b/setup.py index e69d6f87..37eba2cc 100644 --- a/setup.py +++ b/setup.py @@ -1,58 +1,24 @@ #! /usr/bin/env python # -*-coding: utf-8 -*- -import io import glob import os -import re import sys import shutil import subprocess import functools import operator from setuptools import setup -from setuptools.command.test import test as TestCommand from setuptools.command.install import install as _install from distutils.command.build import build as _build from distutils.command.clean import clean as _clean from distutils.command.sdist import sdist as _sdist -def read(*names, **kwargs): - try: - with io.open( - os.path.join(os.path.dirname(__file__), *names), - encoding=kwargs.get("encoding", "utf8") - ) as fp: - return fp.read() - except IOError: - return '' - - -def find_version(*file_paths): - version_file = read(*file_paths) - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) - if version_match: - return version_match.group(1) - raise RuntimeError("Unable to find version string.") - - def platform_is_linux(): return sys.platform.startswith('linux') or \ sys.platform.startswith('darwin') -def get_files_rec(directory): - res = [] - for (path, directory, filenames) in os.walk(directory): - files = [os.path.join(path, fn) for fn in filenames] - res.append((path, files)) - return res - - -data_files = get_files_rec('adslib') - - def create_binaries(): subprocess.call(['make', '-C', 'adslib']) @@ -118,27 +84,7 @@ def run(self): _install.run(self) -class PyTest(TestCommand): - user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")] - - def initialize_options(self): - TestCommand.initialize_options(self) - self.pytest_args = ['--cov-report', 'html', '--cov-report', 'term', - '--cov=pyads'] - - def finalize_options(self): - TestCommand.finalize_options(self) - self.test_args = [] - self.test_suite = True - - def run_tests(self): - import pytest - errno = pytest.main(self.pytest_args) - sys.exit(errno) - - cmdclass = { - 'test': PyTest, 'build': build, 'clean': clean, 'sdist': sdist, @@ -146,37 +92,6 @@ def run_tests(self): } -long_description = read('README.md') - - setup( - name="pyads", - version=find_version('pyads', '__init__.py'), - description="Python wrapper for TwinCAT ADS library", - long_description=long_description, - long_description_content_type='text/markdown', - author="Stefan Lehmann", - author_email="Stefan.St.Lehmann@gmail.com", - packages=["pyads", "pyads.testserver"], - package_data={'pyads': ['adslib.so']}, - requires=[], - install_requires=[], - provides=['pyads'], - url='https://github.com/MrLeeh/pyads', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Software Development :: Libraries', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: Microsoft :: Windows :: Windows 7', - 'Operating System :: POSIX :: Linux', - ], cmdclass=cmdclass, - data_files=data_files, - tests_require=['pytest', 'pytest-cov'], - has_ext_modules=lambda: True, ) diff --git a/tox.ini b/tox.ini index aa05b256..e69de29b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py37, py38, py39, py310 - -[testenv] -commands = discover -deps = discover -changedir = tests -whitelist_externals=* -passenv = TWINCAT3DIR - -[pytest] -testpaths = tests From 9dd6e807cd409916816ba808d1de4a4344289c92 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 25 Jul 2024 15:55:11 +0200 Subject: [PATCH 02/13] Adopted src/ layout --- pyproject.toml | 3 ++- {pyads => src/pyads}/__init__.py | 0 {pyads => src/pyads}/ads.py | 0 {pyads => src/pyads}/connection.py | 0 {pyads => src/pyads}/constants.py | 0 {pyads => src/pyads}/errorcodes.py | 0 {pyads => src/pyads}/filetimes.py | 0 {pyads => src/pyads}/pyads_ex.py | 0 {pyads => src/pyads}/structs.py | 0 {pyads => src/pyads}/symbol.py | 0 {pyads => src/pyads}/testserver/__init__.py | 0 {pyads => src/pyads}/testserver/__main__.py | 0 {pyads => src/pyads}/testserver/advanced_handler.py | 0 {pyads => src/pyads}/testserver/basic_handler.py | 0 {pyads => src/pyads}/testserver/handler.py | 0 {pyads => src/pyads}/testserver/testserver.py | 0 {pyads => src/pyads}/utils.py | 0 17 files changed, 2 insertions(+), 1 deletion(-) rename {pyads => src/pyads}/__init__.py (100%) rename {pyads => src/pyads}/ads.py (100%) rename {pyads => src/pyads}/connection.py (100%) rename {pyads => src/pyads}/constants.py (100%) rename {pyads => src/pyads}/errorcodes.py (100%) rename {pyads => src/pyads}/filetimes.py (100%) rename {pyads => src/pyads}/pyads_ex.py (100%) rename {pyads => src/pyads}/structs.py (100%) rename {pyads => src/pyads}/symbol.py (100%) rename {pyads => src/pyads}/testserver/__init__.py (100%) rename {pyads => src/pyads}/testserver/__main__.py (100%) rename {pyads => src/pyads}/testserver/advanced_handler.py (100%) rename {pyads => src/pyads}/testserver/basic_handler.py (100%) rename {pyads => src/pyads}/testserver/handler.py (100%) rename {pyads => src/pyads}/testserver/testserver.py (100%) rename {pyads => src/pyads}/utils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index cb9aa984..b1d7e3b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,11 +29,12 @@ build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["pyads", "pyads.testserver"] +package-dir = { ""= "src" } [tool.setuptools.package-data] pyads = [ "adslib.so", - "adslib/" + "adslib/**", ] [tool.setuptools.dynamic] diff --git a/pyads/__init__.py b/src/pyads/__init__.py similarity index 100% rename from pyads/__init__.py rename to src/pyads/__init__.py diff --git a/pyads/ads.py b/src/pyads/ads.py similarity index 100% rename from pyads/ads.py rename to src/pyads/ads.py diff --git a/pyads/connection.py b/src/pyads/connection.py similarity index 100% rename from pyads/connection.py rename to src/pyads/connection.py diff --git a/pyads/constants.py b/src/pyads/constants.py similarity index 100% rename from pyads/constants.py rename to src/pyads/constants.py diff --git a/pyads/errorcodes.py b/src/pyads/errorcodes.py similarity index 100% rename from pyads/errorcodes.py rename to src/pyads/errorcodes.py diff --git a/pyads/filetimes.py b/src/pyads/filetimes.py similarity index 100% rename from pyads/filetimes.py rename to src/pyads/filetimes.py diff --git a/pyads/pyads_ex.py b/src/pyads/pyads_ex.py similarity index 100% rename from pyads/pyads_ex.py rename to src/pyads/pyads_ex.py diff --git a/pyads/structs.py b/src/pyads/structs.py similarity index 100% rename from pyads/structs.py rename to src/pyads/structs.py diff --git a/pyads/symbol.py b/src/pyads/symbol.py similarity index 100% rename from pyads/symbol.py rename to src/pyads/symbol.py diff --git a/pyads/testserver/__init__.py b/src/pyads/testserver/__init__.py similarity index 100% rename from pyads/testserver/__init__.py rename to src/pyads/testserver/__init__.py diff --git a/pyads/testserver/__main__.py b/src/pyads/testserver/__main__.py similarity index 100% rename from pyads/testserver/__main__.py rename to src/pyads/testserver/__main__.py diff --git a/pyads/testserver/advanced_handler.py b/src/pyads/testserver/advanced_handler.py similarity index 100% rename from pyads/testserver/advanced_handler.py rename to src/pyads/testserver/advanced_handler.py diff --git a/pyads/testserver/basic_handler.py b/src/pyads/testserver/basic_handler.py similarity index 100% rename from pyads/testserver/basic_handler.py rename to src/pyads/testserver/basic_handler.py diff --git a/pyads/testserver/handler.py b/src/pyads/testserver/handler.py similarity index 100% rename from pyads/testserver/handler.py rename to src/pyads/testserver/handler.py diff --git a/pyads/testserver/testserver.py b/src/pyads/testserver/testserver.py similarity index 100% rename from pyads/testserver/testserver.py rename to src/pyads/testserver/testserver.py diff --git a/pyads/utils.py b/src/pyads/utils.py similarity index 100% rename from pyads/utils.py rename to src/pyads/utils.py From 54c1f5b4fa1621d901e182f17cbeee72f16d42a1 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 25 Jul 2024 16:22:39 +0200 Subject: [PATCH 03/13] Moved requirements --- pyproject.toml | 17 +++++++++ requirements.in | 4 -- requirements.txt | 97 ------------------------------------------------ setup.py | 93 +++++----------------------------------------- 4 files changed, 27 insertions(+), 184 deletions(-) delete mode 100644 requirements.in delete mode 100644 requirements.txt diff --git a/pyproject.toml b/pyproject.toml index b1d7e3b8..1a351c55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,22 @@ classifiers = [ ] license = { text = "MIT" } dynamic = ["version"] +dependencies = [] + +[project.optional-dependencies] +docs = [ + "sphinx", + "sphinx_rtd_theme", + "recommonmark", +] +tests = [ + "pytest", + "pytest-cov", + "tox", +] +dev = [ + "build", +] [project.urls] Repository = "https://github.com/MrLeeh/pyads" @@ -30,6 +46,7 @@ build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["pyads", "pyads.testserver"] package-dir = { ""= "src" } +include-package-data = false [tool.setuptools.package-data] pyads = [ diff --git a/requirements.in b/requirements.in deleted file mode 100644 index c0a48431..00000000 --- a/requirements.in +++ /dev/null @@ -1,4 +0,0 @@ -recommonmark -sphinx -sphinx_rtd_theme -tox diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d4d3fc22..00000000 --- a/requirements.txt +++ /dev/null @@ -1,97 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile -# -alabaster==0.7.12 - # via sphinx -babel==2.9.1 - # via sphinx -backports.entry-points-selectable==1.1.0 - # via virtualenv -certifi==2021.10.8 - # via requests -charset-normalizer==2.0.7 - # via requests -colorama==0.4.4 - # via - # sphinx - # tox -commonmark==0.9.1 - # via recommonmark -distlib==0.3.3 - # via virtualenv -docutils==0.17.1 - # via - # recommonmark - # sphinx - # sphinx-rtd-theme -filelock==3.3.1 - # via - # tox - # virtualenv -idna==3.3 - # via requests -imagesize==1.2.0 - # via sphinx -jinja2==3.0.2 - # via sphinx -markupsafe==2.0.1 - # via jinja2 -packaging==21.0 - # via - # sphinx - # tox -platformdirs==2.4.0 - # via virtualenv -pluggy==1.0.0 - # via tox -py==1.10.0 - # via tox -pygments==2.10.0 - # via sphinx -pyparsing==2.4.7 - # via packaging -pytz==2021.3 - # via babel -recommonmark==0.7.1 - # via -r requirements.in -requests==2.26.0 - # via sphinx -six==1.16.0 - # via - # tox - # virtualenv -snowballstemmer==2.1.0 - # via sphinx -sphinx==4.2.0 - # via - # -r requirements.in - # recommonmark - # sphinx-rtd-theme -sphinx-rtd-theme==1.0.0 - # via -r requirements.in -sphinxcontrib-applehelp==1.0.2 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.0 - # via sphinx -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -toml==0.10.2 - # via tox -tox==3.24.4 - # via -r requirements.in -urllib3==1.26.7 - # via requests -virtualenv==20.8.1 - # via tox - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/setup.py b/setup.py index 37eba2cc..f8bf23d8 100644 --- a/setup.py +++ b/setup.py @@ -1,97 +1,24 @@ -#! /usr/bin/env python -# -*-coding: utf-8 -*- -import glob -import os +from setuptools import setup +from setuptools.command.build import build + import sys -import shutil import subprocess -import functools -import operator -from setuptools import setup -from setuptools.command.install import install as _install -from distutils.command.build import build as _build -from distutils.command.clean import clean as _clean -from distutils.command.sdist import sdist as _sdist def platform_is_linux(): - return sys.platform.startswith('linux') or \ - sys.platform.startswith('darwin') - - -def create_binaries(): - subprocess.call(['make', '-C', 'adslib']) - - -def remove_binaries(): - """Remove all binary files in the adslib directory.""" - patterns = ( - "adslib/*.a", - "adslib/*.o", - "adslib/obj/*.o", - "adslib/*.bin", - "adslib/*.so", - ) + return sys.platform.startswith("linux") or sys.platform.startswith("darwin") - for f in functools.reduce(operator.iconcat, [glob.glob(p) for p in patterns]): - os.remove(f) - -def copy_sharedlib(): - try: - shutil.copy('adslib/adslib.so', 'pyads/adslib.so') - except OSError: - pass - - -def remove_sharedlib(): - try: - os.remove('pyads/adslib.so') - except OSError: - pass - - -class build(_build): +class PyadsBuild(build): def run(self): if platform_is_linux(): - remove_binaries() - create_binaries() - copy_sharedlib() - remove_binaries() - _build.run(self) - - -class clean(_clean): - def run(self): - if platform_is_linux(): - remove_binaries() - remove_sharedlib() - _clean.run(self) - - -class sdist(_sdist): - def run(self): - if platform_is_linux(): - remove_binaries() - _sdist.run(self) - - -class install(_install): - def run(self): - if platform_is_linux(): - create_binaries() - copy_sharedlib() - _install.run(self) - + subprocess.call(["make", "-C", "adslib"]) -cmdclass = { - 'build': build, - 'clean': clean, - 'sdist': sdist, - 'install': install, -} + build.run(self) # Avoid `super()` here for legacy setup( - cmdclass=cmdclass, + cmdclass={ + "build": PyadsBuild, + } ) From f148c373c89e54e2b6dfb8e09b28271cff4fe2d0 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 29 Aug 2024 10:15:30 +0200 Subject: [PATCH 04/13] Struggling to get pyproject.toml to work the same --- .github/workflows/ci.yml | 15 ++--- .github/workflows/python-publish.yml | 8 +-- .gitignore | 2 + pyproject.toml | 8 ++- setup.py | 93 +++++++++++++++++++++++++--- 5 files changed, 101 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 015fa129..4a6fe2f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,24 +21,17 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies + - name: Install package (with dependencies) run: | python -m pip install --upgrade pip - python -m pip install flake8 pytest==7.4.4 coverage coveralls pytest-cov - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Build adslib - run: | - python setup.py build - - name: Install package - run: | - python setup.py develop + pip install .[tests,dev] - name: Test with pytest run: | pytest -v --cov pyads diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 56d2150b..0d450643 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -15,21 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine + pip install build twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | - python setup.py sdist + python -m build twine upload dist/* diff --git a/.gitignore b/.gitignore index 7225462c..74be4ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ build/ *.egg-info/ .eggs/ venv/ +.venv/ +venv_*/ *.tox deploy.py diff --git a/pyproject.toml b/pyproject.toml index 1a351c55..997e1719 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,10 +33,14 @@ tests = [ ] dev = [ "build", + "flake8", + "pytest==7.4.4", + "coverage", + "coveralls", ] [project.urls] -Repository = "https://github.com/MrLeeh/pyads" +Repository = "https://github.com/stlehmann/pyads" Documentation = "https://pyads.readthedocs.io" [build-system] @@ -45,7 +49,7 @@ build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["pyads", "pyads.testserver"] -package-dir = { ""= "src" } +package-dir = { "" = "src" } include-package-data = false [tool.setuptools.package-data] diff --git a/setup.py b/setup.py index f8bf23d8..d882c7ee 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,101 @@ -from setuptools import setup -from setuptools.command.build import build - +import glob +import os import sys +import shutil import subprocess +import functools +import operator +from setuptools import setup +from setuptools.command.install import install as _install +from distutils.command.build import build as _build +from distutils.command.clean import clean as _clean +from distutils.command.sdist import sdist as _sdist def platform_is_linux(): return sys.platform.startswith("linux") or sys.platform.startswith("darwin") -class PyadsBuild(build): +def get_files_rec(directory): + res = [] + for path, directory, filenames in os.walk(directory): + files = [os.path.join(path, fn) for fn in filenames] + res.append((path, files)) + return res + + +def create_binaries(): + subprocess.call(["make", "-C", "adslib"]) + + +def remove_binaries(): + """Remove all binary files in the adslib directory.""" + patterns = ( + "adslib/*.a", + "adslib/*.o", + "adslib/obj/*.o", + "adslib/*.bin", + "adslib/*.so", + ) + + for f in functools.reduce(operator.iconcat, [glob.glob(p) for p in patterns]): + os.remove(f) + + +def copy_sharedlib(): + try: + shutil.copy("adslib/adslib.so", "src/pyads/adslib.so") + except OSError: + pass + + +def remove_sharedlib(): + try: + os.remove("adslib.so") + except OSError: + pass + + +class build(_build): + def run(self): + if platform_is_linux(): + remove_binaries() + create_binaries() + copy_sharedlib() + remove_binaries() + _build.run(self) + + +class clean(_clean): + def run(self): + if platform_is_linux(): + remove_binaries() + remove_sharedlib() + _clean.run(self) + + +class sdist(_sdist): def run(self): if platform_is_linux(): - subprocess.call(["make", "-C", "adslib"]) + remove_binaries() + _sdist.run(self) - build.run(self) # Avoid `super()` here for legacy + +class install(_install): + def run(self): + if platform_is_linux(): + create_binaries() + copy_sharedlib() + _install.run(self) +# noinspection PyTypeChecker setup( cmdclass={ - "build": PyadsBuild, - } + "build": build, + "clean": clean, + # "sdist": sdist, + "install": install, + }, + data_files=get_files_rec("adslib"), # Maybe necessary? ) From 0cab5b11ac95da727e5ac8f9c5958bd811674717 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Tue, 17 Sep 2024 15:46:00 +0200 Subject: [PATCH 05/13] Got sdist build to work as expected --- .gitmodules | 2 +- pyproject.toml | 10 ++- setup.py | 177 +++++++++++++++++++------------------ adslib => src/pyads/adslib | 0 4 files changed, 97 insertions(+), 92 deletions(-) rename adslib => src/pyads/adslib (100%) diff --git a/.gitmodules b/.gitmodules index d5e1d1ee..b630daa1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "adslib"] - path = adslib + path = src/pyads/adslib url = https://github.com/stlehmann/ADS.git diff --git a/pyproject.toml b/pyproject.toml index 997e1719..3549df36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,10 +53,12 @@ package-dir = { "" = "src" } include-package-data = false [tool.setuptools.package-data] -pyads = [ - "adslib.so", - "adslib/**", -] +pyads = ["adslib/**"] +# ^ Always include the adslib source, necessary for an sdist (source) package build + +[tool.setuptools.exclude-package-data] +pyads = ["*.a", "*.o", "obj/*", "*.bin", "*.so"] +# ^ But exclude build files from adslib source [tool.setuptools.dynamic] version = {attr = "pyads.__version__"} diff --git a/setup.py b/setup.py index d882c7ee..ca803bdc 100644 --- a/setup.py +++ b/setup.py @@ -1,101 +1,104 @@ -import glob -import os -import sys -import shutil -import subprocess -import functools -import operator from setuptools import setup -from setuptools.command.install import install as _install -from distutils.command.build import build as _build -from distutils.command.clean import clean as _clean -from distutils.command.sdist import sdist as _sdist +from distutils.command.build import build +import sys + +# import glob +# import os +# import shutil +# import subprocess +# import functools +# import operator +# from setuptools.command.install import install as _install +# from distutils.command.clean import clean as _clean +# from distutils.command.sdist import sdist as _sdist def platform_is_linux(): return sys.platform.startswith("linux") or sys.platform.startswith("darwin") -def get_files_rec(directory): - res = [] - for path, directory, filenames in os.walk(directory): - files = [os.path.join(path, fn) for fn in filenames] - res.append((path, files)) - return res - - -def create_binaries(): - subprocess.call(["make", "-C", "adslib"]) - - -def remove_binaries(): - """Remove all binary files in the adslib directory.""" - patterns = ( - "adslib/*.a", - "adslib/*.o", - "adslib/obj/*.o", - "adslib/*.bin", - "adslib/*.so", - ) - - for f in functools.reduce(operator.iconcat, [glob.glob(p) for p in patterns]): - os.remove(f) - - -def copy_sharedlib(): - try: - shutil.copy("adslib/adslib.so", "src/pyads/adslib.so") - except OSError: - pass - - -def remove_sharedlib(): - try: - os.remove("adslib.so") - except OSError: - pass - - -class build(_build): - def run(self): - if platform_is_linux(): - remove_binaries() - create_binaries() - copy_sharedlib() - remove_binaries() - _build.run(self) - - -class clean(_clean): +# def get_files_rec(directory): +# res = [] +# for path, directory, filenames in os.walk(directory): +# files = [os.path.join(path, fn) for fn in filenames] +# res.append((path, files)) +# return res +# +# +# def create_binaries(): +# subprocess.call(["make", "-C", "adslib"]) +# +# +# def remove_binaries(): +# """Remove all binary files in the adslib directory.""" +# patterns = ( +# "adslib/*.a", +# "adslib/*.o", +# "adslib/obj/*.o", +# "adslib/*.bin", +# "adslib/*.so", +# ) +# +# for f in functools.reduce(operator.iconcat, [glob.glob(p) for p in patterns]): +# os.remove(f) +# +# +# def copy_sharedlib(): +# try: +# shutil.copy("adslib/adslib.so", "src/pyads/adslib.so") +# except OSError: +# pass +# +# +# def remove_sharedlib(): +# try: +# os.remove("adslib.so") +# except OSError: +# pass +# +# +# class build(_build): +# def run(self): +# if platform_is_linux(): +# remove_binaries() +# create_binaries() +# copy_sharedlib() +# remove_binaries() +# _build.run(self) +# +# +# class clean(_clean): +# def run(self): +# if platform_is_linux(): +# remove_binaries() +# remove_sharedlib() +# _clean.run(self) +# +# +# class sdist(_sdist): +# def run(self): +# if platform_is_linux(): +# remove_binaries() +# _sdist.run(self) +# +# +# class install(_install): +# def run(self): +# if platform_is_linux(): +# create_binaries() +# copy_sharedlib() +# _install.run(self) + + +class CustomBuild(build): + """Compile adslib (but only for Linux).""" def run(self): - if platform_is_linux(): - remove_binaries() - remove_sharedlib() - _clean.run(self) - - -class sdist(_sdist): - def run(self): - if platform_is_linux(): - remove_binaries() - _sdist.run(self) - + pass + # if platform_is_linux(): -class install(_install): - def run(self): - if platform_is_linux(): - create_binaries() - copy_sharedlib() - _install.run(self) # noinspection PyTypeChecker setup( - cmdclass={ - "build": build, - "clean": clean, - # "sdist": sdist, - "install": install, - }, - data_files=get_files_rec("adslib"), # Maybe necessary? + cmdclass={}, ) diff --git a/adslib b/src/pyads/adslib similarity index 100% rename from adslib rename to src/pyads/adslib From 12299279292d1470f5f1752a045f08b11606d841 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 19 Sep 2024 10:38:30 +0200 Subject: [PATCH 06/13] Got windows/linux wheels to work --- pyproject.toml | 4 +- setup.py | 158 +++++++++++++++++++++++-------------------------- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3549df36..ec183d14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,9 @@ pyads = ["adslib/**"] # ^ Always include the adslib source, necessary for an sdist (source) package build [tool.setuptools.exclude-package-data] -pyads = ["*.a", "*.o", "obj/*", "*.bin", "*.so"] +pyads = [ + "*.a", "*.o", "obj/*", "*.bin", "*.so" +] # ^ But exclude build files from adslib source [tool.setuptools.dynamic] diff --git a/setup.py b/setup.py index ca803bdc..65c393ad 100644 --- a/setup.py +++ b/setup.py @@ -1,104 +1,96 @@ +from pathlib import Path from setuptools import setup from distutils.command.build import build +from distutils.command.install import install +from wheel.bdist_wheel import bdist_wheel import sys +import sysconfig +import os +import subprocess -# import glob -# import os -# import shutil -# import subprocess -# import functools -# import operator -# from setuptools.command.install import install as _install -# from distutils.command.clean import clean as _clean -# from distutils.command.sdist import sdist as _sdist + +adslib_relative = "src/pyads/adslib" +adslib_root = Path(__file__).parent.absolute() / adslib_relative def platform_is_linux(): return sys.platform.startswith("linux") or sys.platform.startswith("darwin") -# def get_files_rec(directory): -# res = [] -# for path, directory, filenames in os.walk(directory): -# files = [os.path.join(path, fn) for fn in filenames] -# res.append((path, files)) -# return res -# -# -# def create_binaries(): -# subprocess.call(["make", "-C", "adslib"]) -# -# -# def remove_binaries(): -# """Remove all binary files in the adslib directory.""" -# patterns = ( -# "adslib/*.a", -# "adslib/*.o", -# "adslib/obj/*.o", -# "adslib/*.bin", -# "adslib/*.so", -# ) -# -# for f in functools.reduce(operator.iconcat, [glob.glob(p) for p in patterns]): -# os.remove(f) -# -# -# def copy_sharedlib(): -# try: -# shutil.copy("adslib/adslib.so", "src/pyads/adslib.so") -# except OSError: -# pass -# -# -# def remove_sharedlib(): -# try: -# os.remove("adslib.so") -# except OSError: -# pass -# -# -# class build(_build): -# def run(self): -# if platform_is_linux(): -# remove_binaries() -# create_binaries() -# copy_sharedlib() -# remove_binaries() -# _build.run(self) -# -# -# class clean(_clean): -# def run(self): -# if platform_is_linux(): -# remove_binaries() -# remove_sharedlib() -# _clean.run(self) -# -# -# class sdist(_sdist): -# def run(self): -# if platform_is_linux(): -# remove_binaries() -# _sdist.run(self) -# -# -# class install(_install): -# def run(self): -# if platform_is_linux(): -# create_binaries() -# copy_sharedlib() -# _install.run(self) +def create_binaries(): + # Use `make` to build adslib + # Build is done in-place, afterward e.g. `src/pyads/adslib/adslib.so` will exist + subprocess.call(["make", "-C", adslib_relative]) + + +def remove_binaries(): + """Remove all binary files in the adslib directory.""" + patterns = ( + "*.a", + "**/*.o", + "*.bin", + "*.so", + ) + for pattern in patterns: + for file in adslib_root.glob(pattern): + os.remove(file) class CustomBuild(build): """Compile adslib (but only for Linux).""" def run(self): - pass - # if platform_is_linux(): + if platform_is_linux(): + remove_binaries() + create_binaries() + + build.run(self) # Don't use `super()` for compatibility + + +class CustomInstall(install): + """Install compiled adslib (but only for Linux).""" + def run(self): + if platform_is_linux(): + adslib_lib = adslib_root / "adslib.so" + adslib_dest = Path(self.install_lib) + if not adslib_dest.exists(): + adslib_dest.mkdir(parents=True) + self.copy_file( + str(adslib_lib), + str(adslib_dest), + ) + install.run(self) + + +class CustomBDistWheel(bdist_wheel): + """Manually mark our wheel for a specific platform.""" + + def get_tag(self): + """ + + See https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/ + """ + impl_tag = "py2.py3" # Same wheel across Python versions + abi_tag = "none" # Same wheeel across ABI versions (not a C-extension) + # But we need to differentiate on the platform for the compiled adslib: + plat_tag = sysconfig.get_platform().replace("-", "_").replace(".", "_") + + if plat_tag.startswith("linux_"): + # But the basic Linux prefix is deprecated, use new scheme instead: + plat_tag = "manylinux_2_24" + plat_tag[5:] + + # MacOS platform tags area already okay + + # We also keep Windows tags in place, instead of using `any`, to prevent an + # obscure Linux platform to getting a wheel without adslib source + return impl_tag, abi_tag, plat_tag # noinspection PyTypeChecker setup( - cmdclass={}, + cmdclass={ + "build": CustomBuild, + "install": CustomInstall, + "bdist_wheel": CustomBDistWheel, + }, ) From 6246006e3da53100f48597ad9f30ccbe60da8a9c Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Thu, 19 Sep 2024 11:47:51 +0200 Subject: [PATCH 07/13] Got adslib source to be skipped in sdist build --- pyproject.toml | 10 +++++----- setup.py | 24 ++++++++++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ec183d14..a9774403 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,23 +44,23 @@ Repository = "https://github.com/stlehmann/pyads" Documentation = "https://pyads.readthedocs.io" [build-system] -requires = ["setuptools >= 61.0", "setuptools-scm"] +requires = ["setuptools >= 61.0", "setuptools-scm", "wheel"] build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["pyads", "pyads.testserver"] package-dir = { "" = "src" } -include-package-data = false +include-package-data = true [tool.setuptools.package-data] pyads = ["adslib/**"] -# ^ Always include the adslib source, necessary for an sdist (source) package build +# The adslib source is not always needed, it is handled in `setup.py` instead [tool.setuptools.exclude-package-data] pyads = [ - "*.a", "*.o", "obj/*", "*.bin", "*.so" + "*.a", "*.o", "obj/*", "*.bin", "*.so", ] -# ^ But exclude build files from adslib source +# ^ But always exclude build files from adslib source [tool.setuptools.dynamic] version = {attr = "pyads.__version__"} diff --git a/setup.py b/setup.py index 65c393ad..a2ebdbea 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,12 @@ from pathlib import Path from setuptools import setup -from distutils.command.build import build -from distutils.command.install import install +from setuptools.command.install import install +from setuptools.command.build_py import build_py +from setuptools.command.build import build from wheel.bdist_wheel import bdist_wheel import sys import sysconfig +import shutil import os import subprocess @@ -43,7 +45,7 @@ def run(self): remove_binaries() create_binaries() - build.run(self) # Don't use `super()` for compatibility + super().run() class CustomInstall(install): @@ -58,7 +60,7 @@ def run(self): str(adslib_lib), str(adslib_dest), ) - install.run(self) + super().run() class CustomBDistWheel(bdist_wheel): @@ -86,10 +88,24 @@ def get_tag(self): return impl_tag, abi_tag, plat_tag +class CustomBuildPy(build_py): + """Skip adslib during source selection for wheel build.""" + + def run(self): + super().run() + + # If this is run as part of a wheel build, simply remove "adslib/" from + # the build folder + if isinstance(self.distribution.get_command_obj('bdist_wheel'), bdist_wheel): + adslib_dir = Path(self.build_lib) / "pyads" / "adslib" + shutil.rmtree(adslib_dir) + + # noinspection PyTypeChecker setup( cmdclass={ "build": CustomBuild, + "build_py": CustomBuildPy, "install": CustomInstall, "bdist_wheel": CustomBDistWheel, }, From c0632f5d56f387ec24be1f906d0c7347fcd254a3 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Fri, 20 Sep 2024 15:01:21 +0200 Subject: [PATCH 08/13] Got package to work as wanted using MANIFEST.in --- .gitmodules | 2 +- MANIFEST.in | 9 +++++++++ src/pyads/adslib => adslib | 0 pyproject.toml | 14 ++++++-------- setup.py | 20 +++----------------- 5 files changed, 19 insertions(+), 26 deletions(-) create mode 100644 MANIFEST.in rename src/pyads/adslib => adslib (100%) diff --git a/.gitmodules b/.gitmodules index b630daa1..d5e1d1ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "adslib"] - path = src/pyads/adslib + path = adslib url = https://github.com/stlehmann/ADS.git diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..22804535 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,9 @@ +# +# Recipe for extra files to pack for setuptools. +# MANIFEST.in is a little outdated but the best solution to differentiate files between sdist and wheel builds. +# + +graft adslib/ +global-exclude *.a *.o *.obj *.bin *.so +prune obj/ +prune tests/ diff --git a/src/pyads/adslib b/adslib similarity index 100% rename from src/pyads/adslib rename to adslib diff --git a/pyproject.toml b/pyproject.toml index a9774403..c1d393fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,23 +44,21 @@ Repository = "https://github.com/stlehmann/pyads" Documentation = "https://pyads.readthedocs.io" [build-system] -requires = ["setuptools >= 61.0", "setuptools-scm", "wheel"] +requires = ["setuptools >= 61.0", "wheel"] build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["pyads", "pyads.testserver"] package-dir = { "" = "src" } include-package-data = true +# ^ needed for MANIFEST.in -[tool.setuptools.package-data] -pyads = ["adslib/**"] -# The adslib source is not always needed, it is handled in `setup.py` instead +#[tool.setuptools.package-data] +# Package data (adslib/) is handled by MANIFEST.in instead [tool.setuptools.exclude-package-data] -pyads = [ - "*.a", "*.o", "obj/*", "*.bin", "*.so", -] -# ^ But always exclude build files from adslib source +pyads = ["adslib/**"] +# ^ Odd trick, put this excludes the adslib source again from a wheel build and from a pip install [tool.setuptools.dynamic] version = {attr = "pyads.__version__"} diff --git a/setup.py b/setup.py index a2ebdbea..20134535 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,15 @@ from pathlib import Path from setuptools import setup from setuptools.command.install import install -from setuptools.command.build_py import build_py from setuptools.command.build import build from wheel.bdist_wheel import bdist_wheel import sys import sysconfig -import shutil import os import subprocess -adslib_relative = "src/pyads/adslib" +adslib_relative = "adslib" adslib_root = Path(__file__).parent.absolute() / adslib_relative @@ -88,25 +86,13 @@ def get_tag(self): return impl_tag, abi_tag, plat_tag -class CustomBuildPy(build_py): - """Skip adslib during source selection for wheel build.""" - - def run(self): - super().run() - - # If this is run as part of a wheel build, simply remove "adslib/" from - # the build folder - if isinstance(self.distribution.get_command_obj('bdist_wheel'), bdist_wheel): - adslib_dir = Path(self.build_lib) / "pyads" / "adslib" - shutil.rmtree(adslib_dir) - - # noinspection PyTypeChecker setup( cmdclass={ "build": CustomBuild, - "build_py": CustomBuildPy, "install": CustomInstall, "bdist_wheel": CustomBDistWheel, }, ) + +# Also see `MANIFEST.in` From 0729e968ec328f01985430a87643cc2eb5ca92da Mon Sep 17 00:00:00 2001 From: Robert Roos Date: Fri, 20 Sep 2024 16:19:48 +0200 Subject: [PATCH 09/13] Changed adslib DLL finder logic --- src/pyads/pyads_ex.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/pyads/pyads_ex.py b/src/pyads/pyads_ex.py index 6a7e4459..139c665c 100644 --- a/src/pyads/pyads_ex.py +++ b/src/pyads/pyads_ex.py @@ -81,14 +81,19 @@ ) elif platform_is_linux(): - # try to load local adslib.so in favor to global one - local_adslib = os.path.join(os.path.dirname(__file__), "adslib.so") - if os.path.isfile(local_adslib): - adslib = local_adslib - else: - adslib = "adslib.so" + adslib_path = None - _adsDLL = ctypes.CDLL(adslib) + for p in sys.path: + adslib_path = os.path.join(p, "adslib.so") + if os.path.exists(adslib_path): + break + + if adslib_path is None: + raise OSError(f"Failed to locate `adslib.so` library in {sys.path}") + + # For some reason loading on just "adslib.so" always fails, even if it is under + # sys.path, so manually search for it first + _adsDLL = ctypes.CDLL(adslib_path) NOTEFUNC = ctypes.CFUNCTYPE( None, From 9caa40734bbc9fe36c8681bef4d1ec4f3d988faa Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Mon, 23 Sep 2024 08:27:58 +0200 Subject: [PATCH 10/13] Added package CI --- .github/workflows/packaging.yml | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/packaging.yml diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml new file mode 100644 index 00000000..c49f7706 --- /dev/null +++ b/.github/workflows/packaging.yml @@ -0,0 +1,54 @@ +# +# Test job to see how packaging looks like. +# + +name: Build distributions + +on: [push, pull_request] + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - uses: actions/checkout@v4 + with: + submodules: "true" + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - run: | + python -m pip install -U pip + pip install build + + - run: python -m build --wheel -vv + - if: matrix.os == 'ubuntu-latest' + run: python -m build --sdist -vv + # We only need a single source distribution + + - uses: actions/upload-artifact@v4 + with: + name: dist-${{ matrix.os }} + retention-days: 1 + path: dist + + make_artifact: + name: Combine artifacts + needs: build_wheels + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + # Download all artifacts so far + - uses: actions/upload-artifact@v4 + with: + name: package-all + path: dist From 7692b41a95b1c3d0f0a16ee3e510a15ed208a579 Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Tue, 24 Sep 2024 09:57:33 +0200 Subject: [PATCH 11/13] Replaced build by build_py + Cleanup, should work now for editable installs --- setup.py | 80 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index 20134535..cf90ba34 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from pathlib import Path from setuptools import setup from setuptools.command.install import install -from setuptools.command.build import build +from setuptools.command.build_py import build_py from wheel.bdist_wheel import bdist_wheel import sys import sysconfig @@ -9,39 +9,61 @@ import subprocess -adslib_relative = "adslib" -adslib_root = Path(__file__).parent.absolute() / adslib_relative +src_folder = Path(__file__).parent.absolute() / "src" +# ^ This will be on PATH for editable install +adslib_folder = Path(__file__).parent.absolute() / "adslib" +adslib_file = src_folder / "adslib.so" -def platform_is_linux(): - return sys.platform.startswith("linux") or sys.platform.startswith("darwin") +class CustomBuildPy(build_py): + """Custom command for `build_py`. + This command class is used because it is always run, also for an editable install. + """ -def create_binaries(): - # Use `make` to build adslib - # Build is done in-place, afterward e.g. `src/pyads/adslib/adslib.so` will exist - subprocess.call(["make", "-C", adslib_relative]) + @classmethod + def compile_adslib(cls) -> bool: + """Return `True` if adslib was actually compiled.""" + if cls.platform_is_unix(): + cls._clean_library() + cls._compile_library() + return True + return False -def remove_binaries(): - """Remove all binary files in the adslib directory.""" - patterns = ( - "*.a", - "**/*.o", - "*.bin", - "*.so", - ) - for pattern in patterns: - for file in adslib_root.glob(pattern): - os.remove(file) + @staticmethod + def _compile_library(): + """Use `make` to build adslib - build is done in-place.""" + # Produce `adslib.so`: + subprocess.call(["make", "-C", "adslib"]) + @staticmethod + def _clean_library(): + """Remove all compilation artifacts.""" + patterns = ( + "*.a", + "**/*.o", + "*.bin", + "*.so", + ) + for pattern in patterns: + for file in adslib_folder.glob(pattern): + os.remove(file) + + if adslib_file.is_file(): + os.remove(adslib_file) + + @staticmethod + def platform_is_unix(): + return sys.platform.startswith("linux") or sys.platform.startswith("darwin") -class CustomBuild(build): - """Compile adslib (but only for Linux).""" def run(self): - if platform_is_linux(): - remove_binaries() - create_binaries() + if self.compile_adslib(): + # Move .so file from Git submodule into src/ to have it on PATH: + self.move_file( + str(adslib_folder / "adslib.so"), + str(adslib_file), + ) super().run() @@ -49,13 +71,12 @@ def run(self): class CustomInstall(install): """Install compiled adslib (but only for Linux).""" def run(self): - if platform_is_linux(): - adslib_lib = adslib_root / "adslib.so" + if CustomBuildPy.platform_is_unix(): adslib_dest = Path(self.install_lib) if not adslib_dest.exists(): adslib_dest.mkdir(parents=True) self.copy_file( - str(adslib_lib), + str(adslib_file), str(adslib_dest), ) super().run() @@ -89,10 +110,11 @@ def get_tag(self): # noinspection PyTypeChecker setup( cmdclass={ - "build": CustomBuild, + "build_py": CustomBuildPy, "install": CustomInstall, "bdist_wheel": CustomBDistWheel, }, ) +# See `pyproject.toml` for all package information # Also see `MANIFEST.in` From a5e0963671662d8f88f94ab22c1ba4ebb2855f8e Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Tue, 24 Sep 2024 10:47:07 +0200 Subject: [PATCH 12/13] Added test of produced artifacts --- .github/workflows/packaging.yml | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml index c49f7706..f17e876d 100644 --- a/.github/workflows/packaging.yml +++ b/.github/workflows/packaging.yml @@ -52,3 +52,44 @@ jobs: with: name: package-all path: dist + + test_artifacts: + name: Test distributions + needs: make_artifact + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + # Can't really test with Windows because 'TcAdsDll.dll' will be missing + + steps: + - uses: actions/download-artifact@v4 + with: + name: package-all + path: dist + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + # Now install the package from the local wheels and try to use it: + - run: | + pip install pyads --no-index --find-links ./dist + python -c "import pyads; pyads.Connection(ams_net_id='127.0.0.1.1.1', ams_net_port=851)" + + test_editable: + name: Test editable install + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: "true" + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - run: | + pip install -e . -vv + python -c "import pyads; pyads.Connection(ams_net_id='127.0.0.1.1.1', ams_net_port=851)" From 86d8a7ee3e355ead534e0af2ff4ccdec2ba0382c Mon Sep 17 00:00:00 2001 From: RobertoRoos Date: Wed, 25 Sep 2024 08:23:29 +0200 Subject: [PATCH 13/13] fixup! Got package to work as wanted using MANIFEST.in --- MANIFEST.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 22804535..2a85b7c4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,7 +3,7 @@ # MANIFEST.in is a little outdated but the best solution to differentiate files between sdist and wheel builds. # -graft adslib/ +graft adslib global-exclude *.a *.o *.obj *.bin *.so -prune obj/ -prune tests/ +prune obj +prune tests