From 1c2327ceec64a5dd7d70bcf0c2881692d6d1d242 Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 10:58:01 +0100 Subject: [PATCH 1/6] Modernize install with pyproject.toml and update dependencies --- .gitignore | 2 +- MANIFEST.in | 3 +- docs/installation/install.md | 5 + gitversion.py | 245 ----------------------------------- pyproject.toml | 78 +++++++++++ setup.py | 121 ----------------- src/cfclient/__init__.py | 8 +- 7 files changed, 90 insertions(+), 372 deletions(-) delete mode 100644 gitversion.py create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 6c4183df..d6e8f021 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ build/* cache src/cfclient.egg-info/* src/cfclient/third_party/* -version.json +version.py log_param_doc.json # PC client settings diff --git a/MANIFEST.in b/MANIFEST.in index 6a438917..730e3270 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,4 @@ include README.md recursive-include src/cfclient/configs *.json recursive-include src/cfclient/ui *.ui recursive-include src/cfclient/resources * -include src/cfclient/version.json -include gitversion.py \ No newline at end of file +include src/cfclient/version.py \ No newline at end of file diff --git a/docs/installation/install.md b/docs/installation/install.md index 707c6f34..e86fa52f 100644 --- a/docs/installation/install.md +++ b/docs/installation/install.md @@ -7,6 +7,8 @@ page_id: install This project requires Python 3.10+. +To install on Python 3.13, build tools and Python development headers are required. + There are a few things to sort out on your machine before you can install the client. Please see the appropriate section depending on your environment. @@ -66,6 +68,9 @@ Install git from the [official git website](https://git-scm.com/). Make sure it git --version ``` +If you're using Python 3.13, you need to install [Visual Studio](https://visualstudio.microsoft.com/downloads/). During the installation process, you only need to select the C++ Windows Development workload in the Visual Studio Installer. + + #### Install crazyradio drivers To use Crazyradio you will have to [install the drivers](https://www.bitcraze.io/documentation/repository/crazyradio-firmware/master/building/usbwindows/) diff --git a/gitversion.py b/gitversion.py deleted file mode 100644 index a3d30c79..00000000 --- a/gitversion.py +++ /dev/null @@ -1,245 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2013, 2015, Geoffrey T. Dairiki -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the {organization} nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -''' Compute a valid PEP440 version number based on git history. - -If possible, the version is computed from the output of ``git describe``. -If that is successful, the version string is written to the file -``RELEASE-VERSION``. - -If ``git describe`` fails (most likely because we’re in an unpacked -copy of an sdist rather than in a git working copy) then we fall back -on reading the contents of the ``RELEASE-VERSION`` file. - -Usage -===== - -Somewhat more detailed usage instructions, as well as the most recent -version of this code may be found at:: - - https://github.com/dairiki/gitversion. - -That latest version of this file is available at:: - - https://raw.github.com/dairiki/gitversion/master/gitversion.py - -Author -====== - -Author: Jeff Dairiki - -Based on work by: Douglas Creager - -''' -import errno -import os -import re -from tempfile import TemporaryFile -from subprocess import Popen, PIPE - -__version__ = '1.0.2' -__all__ = ('get_version') - -# Name of file in which the version number is cached -VERSION_CACHE = 'RELEASE-VERSION' - - -class GitError(Exception): - pass - - -class GitNotFound(GitError): - ''' The ``git`` command was not found. - ''' - - -class GitFailed(GitError): - def __str__(self): - return '{cmd!r} failed with exit status {code!r}:\n{output}'.format( - cmd=' '.join(self.cmd), - code=self.returncode, - output=self.detail) - - @property - def cmd(self): - return self.args[0] - - @property - def returncode(self): - return self.args[1] - - @property - def detail(self): - return self.args[2] - - -GIT_DESCRIPION_re = re.compile( - r'''\A \s* - (?P.*?) - (?: - -(?P\d+) - -g(?:[\da-f]+) # SHA - )? - (?P-dirty)? - \s* \Z''', re.X) - -# Valid PEP440 release versions -RELEASE_VERSION_re = re.compile(r'\A\d+(\.\d+)*((?:a|b|c|rc)\d+)?\Z') - - -def get_version(**kwargs): - ''' Calculate a valid PEP440 version number based on git history. - - If possible the version is computed from the output of ``git describe``. - If that is successful, the version string is written to the file - ``RELEASE-VERSION``. - - If ``git describe`` fails (most likely because we’re in an unpacked - copy of an sdist rather than in a git working copy) then we fall back - on reading the contents of the ``RELEASE-VERSION`` file. - - ''' - cached_version = get_cached_version() - git_version = get_git_version(**kwargs) - - if git_version is None: - if cached_version is None: - raise RuntimeError('can not determine version number') - return cached_version - - if cached_version != git_version: - set_cached_version(git_version) - return git_version - - -def get_git_version(**kwargs): - if not os.path.isdir('.git'): - # Bail if we're not in the top-level git directory. - # This avoids trouble when setup.py is being run, e.g., in a - # tox build directory which is a subdirectory of (some other) - # git-controlled directory. - return None - - # This check is now redundant, but what the hell. (If I delete - # it, I'll forget the name of the 'rev-parse --is-inside-work-tree'' - # check.) - try: - run_git('rev-parse', '--is-inside-work-tree', **kwargs) - except GitError: # pragma: no cover - # not a git repo, or 'git' command not found - return None - try: - output = run_git('describe', '--tags', '--dirty', **kwargs) - except GitFailed as ex: - if ex.returncode != 128: - raise # pragma: no cover - # No releases have been tagged - return '0.dev%d' % get_number_of_commits_in_head(**kwargs) - - output = ''.join(output).strip() - m = GIT_DESCRIPION_re.match(output) - if not m: - raise GitError( # pragma: no cover - 'can not parse the output of git describe (%r)' % output) - - release, post, dirty = m.groups() - post = int(post) if post else 0 - - if not RELEASE_VERSION_re.match(release): - raise GitError( # pragma: no cover - 'invalid release version (%r)' % release) - - version = release - try: - output = run_git('rev-parse', '--short', 'HEAD', **kwargs) - output = ''.join(output).strip() - except GitFailed as ex: - if ex.returncode != 128: - raise # pragma: no cover - - if dirty: - version += '.post%d.dev0+%s' % (post + 1, output) - elif post: - version += '.post%d+%s' % (post, output) - return version - - -def get_number_of_commits_in_head(**kwargs): - try: - return len(run_git('rev-list', 'HEAD', **kwargs)) - except GitFailed as ex: - if ex.returncode != 128: - raise - return 0 - - -def run_git(*args, **kwargs): - git_cmd = kwargs.get('git_cmd', 'git') - cwd = kwargs.get('cwd') - cmd = (git_cmd,) + args - with TemporaryFile() as stderr: - try: - proc = Popen(cmd, stdout=PIPE, stderr=stderr, cwd=cwd) - except OSError as ex: - if ex.errno == errno.ENOENT: - raise GitNotFound('%r not found in PATH' % git_cmd) - raise - try: - output = [line.decode('latin-1') for line in proc.stdout] - if proc.wait() != 0: - stderr.seek(0) - errout = stderr.read().decode('latin-1') - raise GitFailed(cmd, proc.returncode, errout.rstrip()) - return output - finally: - proc.stdout.close() - - -def get_cached_version(): - try: - with open(VERSION_CACHE) as f: - return f.read().strip() - except IOError as ex: - if ex.errno == errno.ENOENT: - return None - raise - - -def set_cached_version(version): - with open(VERSION_CACHE, 'w') as f: - return f.write(version + '\n') - - -if __name__ == '__main__': - print(get_version()) # pragma: no cover diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..1bd55fb6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,78 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel", "setuptools_scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "cfclient" +dynamic = ["version"] +description = "Crazyflie PC client" +authors = [ + { name = "Bitcraze and contributors", email = "contact@bitcraze.io" }, +] + +readme = {file = "README.md", content-type = "text/markdown"} +license = { text = "GPLv2+" } +keywords = ["quadcopter", "crazyflie"] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", + "Topic :: Scientific/Engineering :: Robotics", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: User Interfaces", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Science/Research", + "Intended Audience :: Education", + "Intended Audience :: Developers", + "Operating System :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + + # Supported Python versions + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +requires-python = ">= 3.10" + +dependencies = [ + "cflib>=0.1.27", + "setuptools", + "appdirs~=1.4.0", + "pyzmq~=26.0", + "pyqtgraph~=0.13", + "PyYAML~=6.0.1", + "numpy~=1.26", + "vispy~=0.14.3", + "pyopengl~=3.1.7", # Required for Vispy to work on MacOS Sonoma + "pyserial~=3.5", + "PyQt6~=6.7", + "PyQt6-sip~=13.8", + "pysdl2~=0.9.14 ; platform_system=='Windows' or platform_system=='Darwin'", + "pysdl2-dll==2.24.0 ; platform_system=='Windows' or platform_system=='Darwin'" +] + +[project.urls] +Homepage = "https://www.bitcraze.io" +Documentation = "https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/" +Repository = "https://github.com/bitcraze/crazyflie-clients-python" +Issues = "https://github.com/bitcraze/crazyflie-clients-python/issues" + +[project.optional-dependencies] +dev = ['pre-commit', + 'cx_freeze==5.1.1 ; platform_system=="Windows"', + 'jinja2==2.10.3 ; platform_system=="Windows"' + ] + +[project.scripts] +cfclient = "cfclient.gui:main" +cfheadless = "cfclient.headless:main" +cfloader = "cfloader:main" +cfzmq = "cfzmq:main" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools_scm] +write_to = "src/cfclient/version.py" diff --git a/setup.py b/setup.py deleted file mode 100644 index c1b07d94..00000000 --- a/setup.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from setuptools import setup, find_packages -from glob import glob -import json -import codecs -import sys -import os - -from gitversion import get_version - -from pathlib import Path - -if sys.version_info < (3, 10): - raise "must use python 3.10 or greater" - - -def relative(lst, base=''): - return list(map(lambda x: base + os.path.basename(x), lst)) - - -try: - VERSION = get_version() -except Exception: - VERSION = None - -if not VERSION and not os.path.isfile('src/cfclient/version.json'): - sys.stderr.write("Git is required to install from source.\n" + - "Please clone the project with Git or use one of the\n" + - "release packages (either from pip or a binary build).\n") - raise Exception("Git required.") - -if not VERSION: - versionfile = open('src/cfclient/version.json', 'r', encoding='utf8') - VERSION = json.loads(versionfile.read())['version'] -else: - with codecs.open('src/cfclient/version.json', 'w', encoding='utf8') as f: - f.write(json.dumps({'version': VERSION})) - -package_data = { - 'cfclient.ui': relative(glob('src/cfclient/ui/*.ui')), - 'cfclient.ui.tabs': relative(glob('src/cfclient/ui/tabs/*.ui')), - 'cfclient.ui.widgets': relative(glob('src/cfclient/ui/widgets/*.ui')), - 'cfclient.ui.dialogs': relative(glob('src/cfclient/ui/dialogs/*.ui')), - 'cfclient': relative(glob('src/cfclient/configs/*.json'), 'configs/') + # noqa - relative(glob('src/cfclient/configs/input/*.json'), 'configs/input/') + # noqa - relative(glob('src/cfclient/configs/log/*.json'), 'configs/log/') + # noqa - relative(glob('src/cfclient/resources/*'), 'resources/') + # noqa - relative(glob('src/cfclient/ui/icons/*.png'), 'ui/icons/') + # noqa - relative(glob('src/cfclient/ui/wizards/*.png'), 'ui/wizards/'), # noqa - '': ['README.md'] -} -data_files = [ - ('third_party', glob('src/cfclient/third_party/*')), -] - -# read the contents of README.md file fo use in pypi description -directory = Path(__file__).parent -long_description = (directory / 'README.md').read_text() - -# Initial parameters -setup( - name='cfclient', - description='Bitcraze Cazyflie quadcopter client', - version=VERSION, - author='Bitcraze team', - author_email='contact@bitcraze.se', - url='http://www.bitcraze.io', - - long_description=long_description, - long_description_content_type='text/markdown', - - classifiers=[ - 'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)', # noqa - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ], - - keywords='quadcopter crazyflie', - - package_dir={'': 'src'}, - packages=find_packages('src'), - - entry_points={ - 'console_scripts': [ - 'cfclient=cfclient.gui:main', - 'cfheadless=cfclient.headless:main', - 'cfloader=cfloader:main', - 'cfzmq=cfzmq:main' - ], - }, - - install_requires=['cflib>=0.1.27', - 'setuptools', - 'appdirs~=1.4.0', - 'pyzmq~=26.0', - 'pyqtgraph~=0.13', - 'PyYAML~=6.0.1', - 'numpy~=1.20', - 'vispy~=0.14.2', - 'pyopengl~=3.1.7', # Requires for Vispy to work on MacOS Sonoma - 'pyserial~=3.5', - 'pyqt6>=6.4', - 'PyQt6-sip>=13.4', - - 'pysdl2~=0.9.14 ; platform_system=="Windows" or platform_system=="Darwin"', - 'pysdl2-dll==2.24.0 ; platform_system=="Windows" or platform_system=="Darwin"'], - - # List of dev dependencies - # You can install them by running - # $ pip install -e .[dev] - extras_require={ - 'dev': ['pre-commit', - 'cx_freeze==5.1.1 ; platform_system=="Windows"', - 'jinja2==2.10.3 ; platform_system=="Windows"'], - }, - - package_data=package_data, - - data_files=data_files, -) diff --git a/src/cfclient/__init__.py b/src/cfclient/__init__.py index 936b3020..66a61761 100644 --- a/src/cfclient/__init__.py +++ b/src/cfclient/__init__.py @@ -44,9 +44,11 @@ except pkg_resources.DistributionNotFound: VERSION = "dev" else: - with open(os.path.join(module_path, "version.json")) as f: - VERSION = json.load(f)['version'] - + try: + from .version import __version__ as imported_version + VERSION = imported_version + except ImportError: + VERSION = "dev" try: with open(os.path.join(module_path, "resources/log_param_doc.json")) as f: log_param_doc = json.load(f) From 5ef90e615c7f33d319d4f53291a42c38afd43062 Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 11:17:01 +0100 Subject: [PATCH 2/6] Correct versioning scheme to match existing. Replace deprecated version file writer. --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1bd55fb6..54e62675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,4 +75,5 @@ cfzmq = "cfzmq:main" include-package-data = true [tool.setuptools_scm] -write_to = "src/cfclient/version.py" +version_file = "src/cfclient/version.py" +version_scheme = "no-guess-dev" \ No newline at end of file From 1fea74ea255cc693001cafbdb8c51a0528245008 Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 11:17:18 +0100 Subject: [PATCH 3/6] Correct Visual Studio workload requirement --- docs/installation/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/install.md b/docs/installation/install.md index e86fa52f..5f06bc01 100644 --- a/docs/installation/install.md +++ b/docs/installation/install.md @@ -68,7 +68,7 @@ Install git from the [official git website](https://git-scm.com/). Make sure it git --version ``` -If you're using Python 3.13, you need to install [Visual Studio](https://visualstudio.microsoft.com/downloads/). During the installation process, you only need to select the C++ Windows Development workload in the Visual Studio Installer. +If you're using Python 3.13, you need to install [Visual Studio](https://visualstudio.microsoft.com/downloads/). During the installation process, you only need to select the Desktop Development with C++ workload in the Visual Studio Installer. #### Install crazyradio drivers From ad8a8806ab36309d5b3fc490c6d4027a4475072b Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 11:43:39 +0100 Subject: [PATCH 4/6] PyPI Trusted Publishing --- .github/workflows/python-publish.yml | 10 +++++++--- .github/workflows/test-python-publish.yml | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index cc3d9f5a..19f3e50f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -18,7 +18,12 @@ jobs: deploy: runs-on: ubuntu-latest - + environment: + name: pypi + url: https://pypi.org/p/cfclient + permissions: + id-token: write + steps: - uses: actions/checkout@v4 - name: Set up Python @@ -34,5 +39,4 @@ jobs: - name: Publish package uses: pypa/gh-action-pypi-publish@release/v1 with: - username: __token__ - password: ${{ secrets.PYPI_TOKEN }} + verbose: true diff --git a/.github/workflows/test-python-publish.yml b/.github/workflows/test-python-publish.yml index 444e635f..74740f50 100644 --- a/.github/workflows/test-python-publish.yml +++ b/.github/workflows/test-python-publish.yml @@ -19,6 +19,12 @@ jobs: runs-on: ubuntu-latest + environment: + name: pypi-test + url: https://pypi.org/p/cfclient + permissions: + id-token: write + steps: - uses: actions/checkout@v4 - name: Set up Python @@ -34,6 +40,5 @@ jobs: - name: Publish package to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - username: __token__ - password: ${{ secrets.PYPI_TEST_TOKEN }} repository_url: https://test.pypi.org/legacy/ + verbose: true From 2495989542add21b65f3690aeccf2b112a88870a Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 11:53:37 +0100 Subject: [PATCH 5/6] CI compatibility with new build process. Use Python 3.13 (latest) --- .github/workflows/CI.yml | 17 ++++++++++++----- tools/build/bdist | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 741c4b11..50da6b05 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -23,15 +23,22 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v4 with: - python-version: 3.12 + python-version: 3.13 - - name: Check build and install + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + + - name: Build package + run: | + python3 -m build + + - name: Install package run: | - pip install setuptools - python3 setup.py sdist pip install dist/*.tar.gz - run: docker pull bitcraze/builder diff --git a/tools/build/bdist b/tools/build/bdist index ed4f3109..e90a2838 100755 --- a/tools/build/bdist +++ b/tools/build/bdist @@ -9,7 +9,7 @@ try: script_dir = os.path.dirname(os.path.realpath(__file__)) root = _path.normpath(_path.join(script_dir, '../..')) - subprocess.check_call(['python3', 'setup.py', 'bdist_wheel'], cwd=root) + subprocess.check_call(['python3', '-m', 'build', '--wheel'], cwd=root) print("Wheel built") except subprocess.CalledProcessError as e: From d0048ed15a7af06a0ed4cda0258c1e900957d396 Mon Sep 17 00:00:00 2001 From: Rik Bouwmeester Date: Wed, 20 Nov 2024 11:56:47 +0100 Subject: [PATCH 6/6] Fetch full git history in GH actions for version https://github.com/pypa/setuptools-scm/issues/480 --- .github/workflows/python-publish.yml | 2 ++ .github/workflows/test-python-publish.yml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 19f3e50f..37ae7f6a 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -26,6 +26,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/test-python-publish.yml b/.github/workflows/test-python-publish.yml index 74740f50..3e52d9b9 100644 --- a/.github/workflows/test-python-publish.yml +++ b/.github/workflows/test-python-publish.yml @@ -27,6 +27,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v4 with: @@ -40,5 +42,5 @@ jobs: - name: Publish package to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - repository_url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ verbose: true