From 8980f5be300959272a24222dd2efdfa3260b415b Mon Sep 17 00:00:00 2001 From: Sterling Peet Date: Tue, 6 Aug 2024 16:35:07 -0600 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Update=20to=20match=20latest=20t?= =?UTF-8?q?emplate=20tooling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 8 +- .cookiecutterrc | 98 +++++++++---------- .github/workflows/github-actions.yml | 54 ++++++++-- .gitignore | 61 ++++++------ .pre-commit-config.yaml | 47 ++++----- .readthedocs.yml | 4 + CONTRIBUTING.rst | 1 - MANIFEST.in | 25 ----- README.rst | 6 +- ci/bootstrap.py | 59 +++++------ .../.github/workflows/github-actions.yml | 15 ++- docs/conf.py | 29 +++--- docs/reference/arduino_cli_cmake_wrapper.rst | 2 + docs/usage.rst | 5 +- pyproject.toml | 31 +++--- pytest.ini | 2 + setup.py | 18 +++- src/arduino_cli_cmake_wrapper/__init__.py | 1 + src/arduino_cli_cmake_wrapper/__main__.py | 1 + src/arduino_cli_cmake_wrapper/builder.py | 12 +-- src/arduino_cli_cmake_wrapper/cli.py | 23 ++--- src/arduino_cli_cmake_wrapper/miner.py | 50 +++------- src/arduino_cli_cmake_wrapper/parser.py | 24 ++--- src/arduino_cli_cmake_wrapper/types.py | 4 +- src/arduino_cli_cmake_wrapper/util.py | 1 + tox.ini | 18 ++-- 26 files changed, 294 insertions(+), 305 deletions(-) delete mode 100644 MANIFEST.in diff --git a/.bumpversion.cfg b/.bumpversion.cfg index d870b4d..598318c 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -5,8 +5,8 @@ tag = True message = ๐Ÿ”– Bump version: {current_version} โ†’ {new_version} [bumpversion:file:setup.py] -search = version='{current_version}' -replace = version='{new_version}' +search = 'fallback_version': '{current_version}' +replace = 'fallback_version': '{new_version}' [bumpversion:file (badge):README.rst] search = /v{current_version}.svg @@ -23,3 +23,7 @@ replace = version = release = '{new_version}' [bumpversion:file:src/arduino_cli_cmake_wrapper/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' + +[bumpversion:file:.cookiecutterrc] +search = version: {current_version} +replace = version: {new_version} diff --git a/.cookiecutterrc b/.cookiecutterrc index c881d71..a270f0b 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -15,57 +15,47 @@ # cookiecutter --overwrite-if-exists --config-file=arduino-cli-cmake-wrapper/.cookiecutterrc gh:ionelmc/cookiecutter-pylibrary default_context: - - allow_tests_inside_package: 'no' - appveyor: 'no' - c_extension_function: 'longest' - c_extension_module: '_arduino_cli_cmake_wrapper' - c_extension_optional: 'no' - c_extension_support: 'no' - c_extension_test_pypi: 'no' - c_extension_test_pypi_username: 'SterlingPeet' - codacy: 'no' - codacy_projectid: '[Get ID from https://app.codacy.com/gh/SterlingPeet/arduino-cli-cmake-wrapper/settings]' - codeclimate: 'no' - codecov: 'no' - command_line_interface: 'argparse' - command_line_interface_bin_name: 'arduino-cli-wrapper' - coveralls: 'no' - distribution_name: 'arduino-cli-cmake-wrapper' - email: 'sterling.peet@ae.gatech.edu' - full_name: 'Sterling Lewis Peet' - github_actions: 'yes' - github_actions_osx: 'yes' - github_actions_windows: 'yes' - license: 'Apache Software License 2.0' - linter: 'flake8' - package_name: 'arduino_cli_cmake_wrapper' - pre_commit: 'yes' - pre_commit_formatter: 'black' - project_name: 'Arduino CLI Wrapper for CMake' - project_short_description: 'Arduino Cmake toolchain leveraging ``arduino-cli`` via python wrapper script' - pypi_badge: 'yes' - pypi_disable_upload: 'no' - release_date: 'today' - repo_hosting: 'github.com' - repo_hosting_domain: 'github.com' - repo_main_branch: 'main' - repo_name: 'arduino-cli-cmake-wrapper' - repo_username: 'SterlingPeet' - requiresio: 'no' - scrutinizer: 'no' - setup_py_uses_pytest_runner: 'no' - setup_py_uses_setuptools_scm: 'no' - sphinx_docs: 'yes' - sphinx_docs_hosting: 'https://arduino-cli-cmake-wrapper.readthedocs.io/' - sphinx_doctest: 'no' - sphinx_theme: 'sphinx-rtd-theme' - test_matrix_configurator: 'no' - test_matrix_separate_coverage: 'no' - travis: 'no' - travis_osx: 'no' - version: '0.0.0' - version_manager: 'bump2version' - website: 'https://github.com/SterlingPeet' - year_from: '2022' - year_to: '2022' + c_extension_optional: "no" + c_extension_support: "no" + codacy: "no" + codacy_projectid: "[Get ID from https://app.codacy.com/gh/SterlingPeet/arduino-cli-cmake-wrapper/settings]" + codeclimate: "no" + codecov: "no" + command_line_interface: "argparse" + command_line_interface_bin_name: "arduino-cli-wrapper" + coveralls: "no" + distribution_name: "arduino-cli-cmake-wrapper" + email: "sterling.peet@gatech.edu" + formatter_quote_style: "single" + full_name: "Sterling Lewis Peet" + function_name: "compute" + github_actions: "yes" + github_actions_osx: "yes" + github_actions_windows: "yes" + license: "Apache Software License 2.0" + module_name: "core" + package_name: "arduino_cli_cmake_wrapper" + pre_commit: "yes" + project_name: "Arduino CLI Wrapper for CMake" + project_short_description: "Arduino Cmake toolchain leveraging ``arduino-cli`` via python wrapper script" + pypi_badge: "yes" + pypi_disable_upload: "no" + release_date: "today" + repo_hosting: "github.com" + repo_hosting_domain: "github.com" + repo_main_branch: "main" + repo_name: "arduino-cli-cmake-wrapper" + repo_username: "SterlingPeet" + scrutinizer: "no" + setup_py_uses_setuptools_scm: "yes" + sphinx_docs: "yes" + sphinx_docs_hosting: "https://arduino-cli-cmake-wrapper.readthedocs.io/" + sphinx_doctest: "no" + sphinx_theme: "sphinx-rtd-theme" + test_matrix_separate_coverage: "no" + tests_inside_package: "no" + version: "0.0.0" + version_manager: "bump2version" + website: "https://github.com/SterlingPeet" + year_from: "2022" + year_to: "2024" diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index fa49216..15fc186 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -1,5 +1,5 @@ name: build -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: test: name: ${{ matrix.name }} @@ -34,7 +34,7 @@ jobs: - name: py38 (macos) python: '3.8' toxpython: python3.8 - python_arch: x64 + python_arch: arm64 tox_env: py38 os: macos-latest - name: py39 (ubuntu) @@ -52,7 +52,7 @@ jobs: - name: py39 (macos) python: '3.9' toxpython: python3.9 - python_arch: x64 + python_arch: arm64 tox_env: py39 os: macos-latest - name: py310 (ubuntu) @@ -70,7 +70,7 @@ jobs: - name: py310 (macos) python: '3.10' toxpython: python3.10 - python_arch: x64 + python_arch: arm64 tox_env: py310 os: macos-latest - name: py311 (ubuntu) @@ -88,9 +88,27 @@ jobs: - name: py311 (macos) python: '3.11' toxpython: python3.11 - python_arch: x64 + python_arch: arm64 tox_env: py311 os: macos-latest + - name: py312 (ubuntu) + python: '3.12' + toxpython: python3.12 + python_arch: x64 + tox_env: py312 + os: ubuntu-latest + - name: py312 (windows) + python: '3.12' + toxpython: python3.12 + python_arch: x64 + tox_env: py312 + os: windows-latest + - name: py312 (macos) + python: '3.12' + toxpython: python3.12 + python_arch: arm64 + tox_env: py312 + os: macos-latest - name: pypy38 (ubuntu) python: pypy-3.8 toxpython: pypy3.8 @@ -106,7 +124,7 @@ jobs: - name: pypy38 (macos) python: pypy-3.8 toxpython: pypy3.8 - python_arch: x64 + python_arch: arm64 tox_env: pypy38 os: macos-latest - name: pypy39 (ubuntu) @@ -124,14 +142,32 @@ jobs: - name: pypy39 (macos) python: pypy-3.9 toxpython: pypy3.9 - python_arch: x64 + python_arch: arm64 tox_env: pypy39 os: macos-latest + - name: pypy310 (ubuntu) + python: pypy-3.10 + toxpython: pypy3.10 + python_arch: x64 + tox_env: pypy310 + os: ubuntu-latest + - name: pypy310 (windows) + python: pypy-3.10 + toxpython: pypy3.10 + python_arch: x64 + tox_env: pypy310 + os: windows-latest + - name: pypy310 (macos) + python: pypy-3.10 + toxpython: pypy3.10 + python_arch: arm64 + tox_env: pypy310 + os: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.python_arch }} diff --git a/.gitignore b/.gitignore index 83a43fd..a64fe0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,53 @@ *.py[cod] __pycache__ +# Temp files +.*.sw[po] +*~ +*.bak +.DS_Store + # C extensions *.so -# Packages +# Build and package files *.egg *.egg-info -dist -build -eggs +.bootstrap +.build +.cache .eggs -parts +.env +.installed.cfg +.ve bin -var -sdist -wheelhouse +build develop-eggs -.installed.cfg +dist +eggs lib lib64 -venv*/ -pyvenv*/ +parts pip-wheel-metadata/ +pyvenv*/ +sdist +var +venv*/ +wheelhouse # Installer logs pip-log.txt # Unit test / coverage reports +.benchmarks .coverage -.tox .coverage.* +.pytest .pytest_cache/ -nosetests.xml +.tox coverage.xml htmlcov +nosetests.xml # Translations *.mo @@ -43,12 +56,12 @@ htmlcov .mr.developer.cfg # IDE project files +*.iml +*.komodoproject +.idea .project .pydevproject -.idea .vscode -*.iml -*.komodoproject # Complexity output/*.html @@ -57,18 +70,8 @@ output/*/index.html # Sphinx docs/_build -.DS_Store -*~ -.*.sw[po] -.build -.ve -.env -.cache -.pytest -.benchmarks -.bootstrap -.appveyor.token -*.bak - # Mypy Cache .mypy_cache/ + +# Generated by setuptools-scm +src/*/_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2e330c..035e3d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,9 @@ -# To install the git pre-commit hook run: -# pre-commit install -# To update the pre-commit hooks run: -# -exclude: ^(\.tox|ci|\.bumpversion\.cfg)(/|$) -default_install_hook_types: [pre-commit, pre-merge-commit, pre-push, commit-msg] -default_stages: [pre-commit, pre-merge-commit, pre-push] +# To install the git pre-commit hooks run: +# pre-commit install --install-hooks +# To update the versions: +# pre-commit autoupdate +exclude: ^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$) +# Note the order is intentional to avoid multiple passes of the hooks repos: - repo: https://github.com/SterlingPeet/git-precommit-hooks rev: v1.0.0 @@ -17,8 +16,14 @@ repos: hooks: - id: gitlint stages: [commit-msg] +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.6 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-ast @@ -44,7 +49,7 @@ repos: args: [--autofix] - id: trailing-whitespace - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.10.0 + rev: v2.14.0 hooks: - id: pretty-format-ini args: [--autofix] @@ -52,16 +57,11 @@ repos: args: [--autofix] - id: pretty-format-yaml args: [--autofix, --indent, '2'] -- repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.284 - hooks: - - id: ruff - args: [--fix] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.0 + rev: v1.11.1 hooks: - id: mypy - additional_dependencies: [] + additional_dependencies: [types-setuptools] - repo: https://github.com/PyCQA/docformatter rev: v1.7.5 hooks: @@ -69,7 +69,7 @@ repos: additional_dependencies: [tomli] args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/myint/rstcheck - rev: v6.1.2 + rev: v6.2.4 hooks: - id: rstcheck args: [--report-level=warning, '--ignore-directives=automodule,testsetup'] @@ -78,19 +78,10 @@ repos: rev: 6.3.0 hooks: - id: pydocstyle + exclude: ^ci/ additional_dependencies: [toml] -- repo: https://github.com/psf/black - rev: 23.7.0 - hooks: - - id: black - args: [--skip-string-normalization, --line-length=79] -- repo: https://github.com/asottile/blacken-docs - rev: 1.15.0 - hooks: - - id: blacken-docs - additional_dependencies: [black==23.7.0] - repo: https://github.com/Yelp/detect-secrets - rev: v1.4.0 + rev: v1.5.0 hooks: - id: detect-secrets # args: [--baseline, ci/secrets.baseline] diff --git a/.readthedocs.yml b/.readthedocs.yml index beed33b..1a49ed2 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,6 +3,10 @@ version: 2 sphinx: configuration: docs/conf.py formats: all +build: + os: ubuntu-22.04 + tools: + python: '3' python: install: - requirements: docs/requirements.txt diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 745c209..835bb23 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -73,7 +73,6 @@ For merging, you should: 3. Add yourself to ``AUTHORS.rst``. 4. Fill out the PR template. - Tips ---- diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 82bfe74..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,25 +0,0 @@ -graft docs -graft src -graft ci -graft tests - -include .bumpversion.cfg -include .cookiecutterrc -include .coveragerc -include .editorconfig -include .gitchangelog.rc -include .github/workflows/github-actions.yml -include .gitlint -include .pre-commit-config.yaml -include .readthedocs.yml -include pytest.ini -include tox.ini - -include AUTHORS.rst -include CHANGELOG.rst -include CONTRIBUTING.rst -include DEVELOPER_NOTES.rst -include LICENSE -include README.rst - -global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index d6ad5cc..519416f 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ :stub-columns: 1 * - ๐Ÿ”จ Code - - | |black| |isort| |ruff| |contributors| |commit| |license| |semver| + - | |isort| |ruff| |contributors| |commit| |license| |semver| * - ๐Ÿ“ Docs - | |gitmoji| |docs| |docformatter| |mypy| |docstyle| |gitchangelog| * - ๐Ÿงช Tests @@ -18,10 +18,6 @@ | |commits-since| -.. |black| image:: https://img.shields.io/badge/%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: The Uncompromising Code Formatter - .. |isort| image:: https://img.shields.io/badge/%20imports-isort-%231674b1 :target: https://pycqa.github.io/isort/ :alt: isort your imports, so you don't have to diff --git a/ci/bootstrap.py b/ci/bootstrap.py index fd2ece4..c0cce09 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,51 +1,50 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import os import pathlib import subprocess import sys base_path: pathlib.Path = pathlib.Path(__file__).resolve().parent.parent -templates_path = base_path / "ci" / "templates" +templates_path = base_path / 'ci' / 'templates' def check_call(args): - print("+", *args) + print('+', *args) subprocess.check_call(args) def exec_in_env(): - env_path = base_path / ".tox" / "bootstrap" - if sys.platform == "win32": - bin_path = env_path / "Scripts" + env_path = base_path / '.tox' / 'bootstrap' + if sys.platform == 'win32': + bin_path = env_path / 'Scripts' else: - bin_path = env_path / "bin" + bin_path = env_path / 'bin' if not env_path.exists(): import subprocess - print("Making bootstrap env in: {0} ...".format(env_path)) + print(f'Making bootstrap env in: {env_path} ...') try: - check_call([sys.executable, "-m", "venv", env_path]) + check_call([sys.executable, '-m', 'venv', env_path]) except subprocess.CalledProcessError: try: - check_call([sys.executable, "-m", "virtualenv", env_path]) + check_call([sys.executable, '-m', 'virtualenv', env_path]) except subprocess.CalledProcessError: - check_call(["virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment...") - check_call([bin_path / "pip", "install", "jinja2", "tox"]) - python_executable = bin_path / "python" + check_call(['virtualenv', env_path]) + print('Installing `jinja2` into bootstrap environment...') + check_call([bin_path / 'pip', 'install', 'jinja2', 'tox']) + python_executable = bin_path / 'python' if not python_executable.exists(): python_executable = python_executable.with_suffix('.exe') - print("Re-executing with: {0}".format(python_executable)) - print("+ exec", python_executable, __file__, "--no-env") - os.execv(python_executable, [python_executable, __file__, "--no-env"]) + print(f'Re-executing with: {python_executable}') + print('+ exec', python_executable, __file__, '--no-env') + os.execv(python_executable, [python_executable, __file__, '--no-env']) def main(): import jinja2 - print("Project path: {0}".format(base_path)) + print(f'Project path: {base_path}') jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(str(templates_path)), @@ -53,7 +52,6 @@ def main(): lstrip_blocks=True, keep_trailing_newline=True, ) - tox_environments = [ line.strip() # 'tox' need not be installed globally, but must be importable @@ -61,26 +59,31 @@ def main(): # This uses sys.executable the same way that the call in # cookiecutter-pylibrary/hooks/post_gen_project.py # invokes this bootstrap.py itself. - for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() + for line in subprocess.check_output( + [sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True + ).splitlines() ] tox_environments = [line for line in tox_environments if line.startswith('py')] - for template in templates_path.rglob('*'): if template.is_file(): - template_path = str(template.relative_to(templates_path)) + template_path = template.relative_to(templates_path).as_posix() destination = base_path / template_path destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_text(jinja.get_template(template_path).render(tox_environments=tox_environments)) - print("Wrote {}".format(template_path)) - print("DONE.") + destination.write_text( + jinja.get_template(template_path).render( + tox_environments=tox_environments + ) + ) + print(f'Wrote {template_path}') + print('DONE.') -if __name__ == "__main__": +if __name__ == '__main__': args = sys.argv[1:] - if args == ["--no-env"]: + if args == ['--no-env']: main() elif not args: exec_in_env() else: - print("Unexpected arguments {0}".format(args), file=sys.stderr) + print(f'Unexpected arguments: {args}', file=sys.stderr) sys.exit(1) diff --git a/ci/templates/.github/workflows/github-actions.yml b/ci/templates/.github/workflows/github-actions.yml index 96e0b65..691efb7 100644 --- a/ci/templates/.github/workflows/github-actions.yml +++ b/ci/templates/.github/workflows/github-actions.yml @@ -1,5 +1,5 @@ name: build -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: test: name: {{ '${{ matrix.name }}' }} @@ -22,9 +22,9 @@ jobs: {% for env in tox_environments %} {% set prefix = env.split('-')[0] -%} {% if prefix.startswith('pypy') %} -{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% set cpython %}pp{{ prefix[4:5] }}{% endset %} -{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% else %} {% set python %}'{{ prefix[2] }}.{{ prefix[3:] }}'{% endset %} {% set cpython %}cp{{ prefix[2:] }}{% endset %} @@ -33,22 +33,21 @@ jobs: {% for os, python_arch in [ ['ubuntu', 'x64'], ['windows', 'x64'], - ['macos', 'x64'], + ['macos', 'arm64'], ] %} - name: {{ env }} ({{ os }}) python: {{ python }} toxpython: {{ toxpython }} python_arch: {{ python_arch }} - tox_env: {{ env }}{% if 'cover' in env %},codecov{% endif %} - + tox_env: {{ env }} os: {{ os }}-latest {% endfor %} {% endfor %} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: {{ '${{ matrix.python }}' }} architecture: {{ '${{ matrix.python_arch }}' }} diff --git a/docs/conf.py b/docs/conf.py index 305a0d3..383a136 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,5 @@ """Configure the ``sphinx`` documentation build for the project.""" -import os - extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -16,21 +14,29 @@ source_suffix = '.rst' master_doc = 'index' project = 'Arduino CLI Wrapper for CMake' -year = '2022' +year = '2022-2024' author = 'Sterling Lewis Peet' copyright = f'{year}, {author}' -version = release = '0.0.0' +try: + from pkg_resources import get_distribution + + version = release = get_distribution('arduino_cli_cmake_wrapper').version +except Exception: + import traceback + + traceback.print_exc() + version = release = '0.0.0' pygments_style = 'trac' templates_path = ['.'] extlinks = { 'issue': ( 'https://github.com/SterlingPeet/arduino-cli-cmake-wrapper/issues/%s', - '#', + '#%s', ), 'pr': ( 'https://github.com/SterlingPeet/arduino-cli-cmake-wrapper/pull/%s', - 'PR #', + 'PR #%s', ), } # FIXME: remove these exceptions when the links are made real @@ -38,18 +44,15 @@ r'https://arduino-cli-cmake-wrapper.readthedocs.io/', r'https://pypi.org/project/arduino-cli-cmake-wrapper', ] -# on_rtd is whether we are on readthedocs.org -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -if not on_rtd: # only set the theme if we're building docs locally - html_theme = 'sphinx_rtd_theme' +html_theme = 'sphinx_rtd_theme' +html_theme_options = { + 'githuburl': 'https://github.com/SterlingPeet/arduino-cli-cmake-wrapper/', +} html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = False -html_sidebars = { - '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], -} html_short_title = f'{project}-{version}' napoleon_use_ivar = True diff --git a/docs/reference/arduino_cli_cmake_wrapper.rst b/docs/reference/arduino_cli_cmake_wrapper.rst index f874f9a..6d55205 100644 --- a/docs/reference/arduino_cli_cmake_wrapper.rst +++ b/docs/reference/arduino_cli_cmake_wrapper.rst @@ -7,3 +7,5 @@ arduino_cli_cmake_wrapper .. automodule:: arduino_cli_cmake_wrapper :members: + :undoc-members: + :special-members: __init__, __len__ diff --git a/docs/usage.rst b/docs/usage.rst index 6bae71f..098cf20 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -4,4 +4,7 @@ Usage To use Arduino CLI Wrapper for CMake in a project:: - import arduino_cli_cmake_wrapper +.. code-block:: python + + import arduino_cli_cmake_wrapper + arduino_cli_cmake_wrapper.compute(...) diff --git a/pyproject.toml b/pyproject.toml index 8f22e59..f615cb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,9 @@ [build-system] requires = [ "setuptools>=30.3.0", - "wheel" + "setuptools_scm>=3.3.1" ] -[tool.black] -line-length = 79 -skip-string-normalization = true -target-version = ['py38'] - [tool.docformatter] recursive = true wrap-summaries = 95 @@ -17,14 +12,23 @@ wrap-summaries = 95 ignore_missing_imports = true [tool.ruff] -extend-exclude = [".tox", ".eggs", "static", "ci/templates"] +extend-exclude = ["static", "ci/templates"] +line-length = 89 +src = ["src", "tests"] +target-version = "py38" + +[tool.ruff.format] +quote-style = "single" + +[tool.ruff.lint] ignore = [ "RUF001", # ruff-specific rules ambiguous-unicode-character-string "S101", # flake8-bandit assert "S308", # flake8-bandit suspicious-mark-safe-usage + "S603", # flake8-bandit subprocess-without-shell-equals-true + "S607", # flake8-bandit start-process-with-partial-path "E501" # pycodestyle line-too-long ] -line-length = 89 select = [ "B", # flake8-bugbear "C4", # flake8-comprehensions @@ -46,16 +50,17 @@ select = [ "UP", # pyupgrade "W" # pycodestyle warnings ] -src = ["src", "tests"] -target-version = "py38" -[tool.ruff.flake8-pytest-style] +[tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] inline-quotes = "single" -[tool.ruff.isort] +[tool.ruff.lint.isort] force-single-line = true forced-separate = ["conftest"] + +[tool.ruff.lint.per-file-ignores] +"ci/*" = ["S"] diff --git a/pytest.ini b/pytest.ini index d0a6e92..ab31551 100644 --- a/pytest.ini +++ b/pytest.ini @@ -18,6 +18,8 @@ addopts = --tb=short testpaths = tests +# If you want to switch back to tests outside package just remove --pyargs +# and edit testpaths to have "tests/" instead of "arduino_cli_cmake_wrapper". # Idea from: https://til.simonwillison.net/pytest/treat-warnings-as-errors filterwarnings = diff --git a/setup.py b/setup.py index 2c3553e..043882a 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Setup script to install the ``arduino-cli-cmake-wrapper`` package.""" import re @@ -18,7 +18,11 @@ def read(*names, **kwargs): setup( name='arduino-cli-cmake-wrapper', - version='0.0.0', + use_scm_version={ + 'local_scheme': 'dirty-tag', + 'write_to': 'src/arduino_cli_cmake_wrapper/_version.py', + 'fallback_version': '0.0.0', + }, license='Apache-2.0', description='Arduino Cmake toolchain leveraging ``arduino-cli`` via python wrapper script', long_description='{}\n{}'.format( @@ -28,7 +32,7 @@ def read(*names, **kwargs): re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')), ), author='Sterling Lewis Peet', - author_email='sterling.peet@ae.gatech.edu', + author_email='sterling.peet@gatech.edu', url='https://github.com/SterlingPeet/arduino-cli-cmake-wrapper', packages=find_packages('src'), package_dir={'': 'src'}, @@ -50,6 +54,7 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', @@ -68,9 +73,12 @@ def read(*names, **kwargs): ], extras_require={ # eg: - # 'rst': ['docutils>=0.11'], - # ':python_version=="2.6"': ['argparse'], + # "rst": ["docutils>=0.11"], + # ":python_version=='3.8'": ["backports.zoneinfo"], }, + setup_requires=[ + 'setuptools_scm>=3.3.1', + ], entry_points={ 'console_scripts': [ 'arduino-cli-wrapper = arduino_cli_cmake_wrapper.cli:main', diff --git a/src/arduino_cli_cmake_wrapper/__init__.py b/src/arduino_cli_cmake_wrapper/__init__.py index a34b17f..e4246b6 100644 --- a/src/arduino_cli_cmake_wrapper/__init__.py +++ b/src/arduino_cli_cmake_wrapper/__init__.py @@ -24,4 +24,5 @@ this to avoid hard dependence on the exact version or Arduino, Arduino Core, and the specific versions of installed libraries. """ + __version__ = '0.0.0' diff --git a/src/arduino_cli_cmake_wrapper/__main__.py b/src/arduino_cli_cmake_wrapper/__main__.py index d8e5f6e..f45f5e2 100644 --- a/src/arduino_cli_cmake_wrapper/__main__.py +++ b/src/arduino_cli_cmake_wrapper/__main__.py @@ -1,4 +1,5 @@ """Entrypoint module, in case you use `python -marduino_cli_cmake_wrapper`.""" + from arduino_cli_cmake_wrapper.cli import main if __name__ == '__main__': diff --git a/src/arduino_cli_cmake_wrapper/builder.py b/src/arduino_cli_cmake_wrapper/builder.py index e6d7c3e..18628c4 100644 --- a/src/arduino_cli_cmake_wrapper/builder.py +++ b/src/arduino_cli_cmake_wrapper/builder.py @@ -7,6 +7,7 @@ @author lestarch """ + import logging import subprocess from pathlib import Path @@ -40,8 +41,7 @@ def make_sketch(directory: Path, libraries: List[str]) -> Dict[Source, Path]: """ libraries = libraries if libraries else [] mappings = { - source: directory / f'{directory.name}.{ source.value }' - for source in Source + source: directory / f'{directory.name}.{ source.value }' for source in Source } # Create an empty source of each type for _, path in mappings.items(): @@ -50,9 +50,7 @@ def make_sketch(directory: Path, libraries: List[str]) -> Dict[Source, Path]: assert ino_file is not None # Create main sketch as an ino file such that it is a valid sketch with Path.open(ino_file, 'w') as file_handle: - include_set = '\n'.join( - f'#include <{library}.h>' for library in libraries - ) + include_set = '\n'.join(f'#include <{library}.h>' for library in libraries) file_handle.write(include_set + '\nvoid setup() {}\nvoid loop() {}') return mappings @@ -89,9 +87,7 @@ def compile_sketch( ] LOGGER.debug('Invoking: %s', ' '.join(arguments)) - process = subprocess.run( - arguments, text=True, capture_output=True # noqa: S603 - ) + process = subprocess.run(arguments, text=True, capture_output=True) if process.returncode != 0: raise FauxBuildException( f'arduino-cli failed with return code: {process.returncode}', diff --git a/src/arduino_cli_cmake_wrapper/cli.py b/src/arduino_cli_cmake_wrapper/cli.py index 4fbfd73..938b460 100644 --- a/src/arduino_cli_cmake_wrapper/cli.py +++ b/src/arduino_cli_cmake_wrapper/cli.py @@ -5,6 +5,7 @@ This allows the program to know how arduino should compile C and C++ files. """ + import argparse import json import logging @@ -196,9 +197,7 @@ def remap_output( 'Source.S': 'ASM', 'Source.INO': 'INO', str(cache_path): str(output_directory), - str( - output_directory / f'{test_files[Source.INO].name}.elf' - ): '', + str(output_directory / f'{test_files[Source.INO].name}.elf'): '', str(output_directory / test_files[Source.INO].name): '', str(test_files[Source.INO].name): '', } @@ -238,9 +237,7 @@ def assemble_output_data( assembled data. """ compilers, includes, build_flags = build_tokens(stages, test_files) - linker, link_flags, link_objects, link_libraries = link_tokens( - stages, test_files - ) + linker, link_flags, link_objects, link_libraries = link_tokens(stages, test_files) archiver, archive_flags = archive_tokens(stages) post_link_steps = post_link_lines(stages, test_files) @@ -266,9 +263,9 @@ def main(arguments: Optional[List[str]] = None): try: args = parse_arguments(arguments) output_directory = Path(args.output) - logging.basicConfig( - level=logging.DEBUG - ) if args.debug else logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.DEBUG) if args.debug else logging.basicConfig( + level=logging.INFO + ) # Run the build test_file_map, stdout, stderr = build(args.board, args.libraries, []) @@ -292,11 +289,9 @@ def main(arguments: Optional[List[str]] = None): output_data['arguments'] = sys.argv[1:] # Output the data as JSON - with ( - Path.open(args.json_file, 'w') - if args.json_file is not None - else sys.stdout - ) as file_handle: + with Path.open( + args.json_file, 'w' + ) if args.json_file is not None else sys.stdout as file_handle: json.dump(output_data, file_handle, indent=4) # Outputs the sketch cache for ingestion into larger build diff --git a/src/arduino_cli_cmake_wrapper/miner.py b/src/arduino_cli_cmake_wrapper/miner.py index 8c87003..fd007d3 100644 --- a/src/arduino_cli_cmake_wrapper/miner.py +++ b/src/arduino_cli_cmake_wrapper/miner.py @@ -2,6 +2,7 @@ Handles the data mining operations on the output of the build stages. """ + import logging from functools import partial from pathlib import Path @@ -106,9 +107,7 @@ def filterer(item: str): def build_tokens( stages: Dict[Stage, List[str]], sources: Dict[Source, Path] -) -> Tuple[ - Dict[Source, str], Dict[Source, List[str]], Dict[Source, List[str]] -]: +) -> Tuple[Dict[Source, str], Dict[Source, List[str]], Dict[Source, List[str]]]: """Detect command line build tokens for each source type. Looks at the source build invocation for each file in the source @@ -139,9 +138,7 @@ def build_tokens( class Cleaner(FilterProtocol): """Clean out -c -o and filename arguments.""" - def __call__( - self, tokens: List[str], negate: bool = False - ) -> List[str]: + def __call__(self, tokens: List[str], negate: bool = False) -> List[str]: """Clean out -c -o and filename arguments.""" return filter_by_filenames( list(real_names.values()), @@ -149,16 +146,13 @@ def __call__( ) cleaner = Cleaner() - sorter = partial(filter_by_flags, {'-I': lambda item: item == '-I'}) + sorter = partial(filter_by_flags, {'-I': lambda item: item == '-I'}) # type: ignore[dict-item] sorted_source_lines = { - source: sort_line(line, cleaner, sorter) - for source, line in source_lines.items() + source: sort_line(line, cleaner, sorter) for source, line in source_lines.items() } tools = {source: sorted_source_lines[source][0] for source in Source} - non_include_paths = { - source: sorted_source_lines[source][1] for source in Source - } + non_include_paths = {source: sorted_source_lines[source][1] for source in Source} include_paths = { source: [ path.replace('-I', '') @@ -171,12 +165,8 @@ def __call__( 'Detected compilers:\n\t%s', '\n\t'.join([f'{source}: {value}' for source, value in tools.items()]), ) - LOGGER.debug( - 'Detected include paths:%s', string_dictionary_of_list(include_paths) - ) - LOGGER.debug( - 'Detected build flags:%s', string_dictionary_of_list(non_include_paths) - ) + LOGGER.debug('Detected include paths:%s', string_dictionary_of_list(include_paths)) + LOGGER.debug('Detected build flags:%s', string_dictionary_of_list(non_include_paths)) return tools, include_paths, non_include_paths @@ -198,9 +188,7 @@ def sketch_cache(stages: Dict[Stage, List[str]]) -> Path: raise MissingStageException(Stage.CORE) core_tokens = [ - token - for token in safe_split(core_lines[-1]) - if token.endswith('core.a') + token for token in safe_split(core_lines[-1]) if token.endswith('core.a') ] assert core_tokens, 'Could not find core.a' core_archive = core_tokens[0] @@ -277,9 +265,7 @@ def sort_line( return (tokens[0], sort(tokens[1:]), sort(tokens[1:], negate=True)) -def identify_link_line( - stages: Dict[Stage, List[str]], source_objects: List[str] -) -> str: +def identify_link_line(stages: Dict[Stage, List[str]], source_objects: List[str]) -> str: """Identify the linking line based on source object names. This function identifies the linking line within the context of the @@ -299,9 +285,7 @@ def identify_link_line( identify_line: A general function to identify lines based on stages and criteria. """ - link_line = identify_line( - Stage.LINK, stages, partial(match_all, source_objects) - ) + link_line = identify_line(Stage.LINK, stages, partial(match_all, source_objects)) LOGGER.debug('Linking line: %s', link_line) return link_line @@ -354,17 +338,13 @@ def link_tokens( identified linker, a list of link flags, a list of link objects, and a list of link libraries. """ - object_names = [ - f'{name}.o' for name in real_source_names(sources).values() - ] + object_names = [f'{name}.o' for name in real_source_names(sources).values()] link_line = identify_link_line(stages, object_names) class Cleaner(FilterProtocol): """Filter to clean out -c -o and filename arguments.""" - def __call__( - self, tokens: List[str], negate: bool = False - ) -> List[str]: + def __call__(self, tokens: List[str], negate: bool = False) -> List[str]: """Filter to clean out -c -o and filename arguments.""" return filter_by_filenames( object_names, filter_by_flags({'-o': True}, tokens) @@ -448,9 +428,7 @@ def post_link_lines( identify_link_line: A function to identify the linking line based on source object names. """ - object_names = [ - f'{name}.o' for name in real_source_names(sources).values() - ] + object_names = [f'{name}.o' for name in real_source_names(sources).values()] link_line = identify_link_line(stages, object_names) stage_lines = stages.get(Stage.LINK, []) post_links = stage_lines[stage_lines.index(link_line) + 1 :] diff --git a/src/arduino_cli_cmake_wrapper/parser.py b/src/arduino_cli_cmake_wrapper/parser.py index f511938..afba8db 100644 --- a/src/arduino_cli_cmake_wrapper/parser.py +++ b/src/arduino_cli_cmake_wrapper/parser.py @@ -10,6 +10,7 @@ @author lestarch """ + import logging from shutil import which from typing import Dict @@ -49,17 +50,13 @@ def sectioner(stdout: str) -> List[Tuple[str, List[str]]]: list of (announcement, section lines) tuples containing the in-order build section output """ lines = [line.strip() for line in stdout.split('\n')] - titles = filter( - lambda enumeration: enumeration[1].endswith('...'), enumerate(lines) - ) + titles = filter(lambda enumeration: enumeration[1].endswith('...'), enumerate(lines)) enumerated_titles = list(enumerate(titles)) indexed_titles = [ (title, start, dict(enumerated_titles).get(index + 1, (-1,))[0]) for index, (start, title) in enumerated_titles ] - return [ - (title, lines[start + 1 : end]) for title, start, end in indexed_titles - ] + return [(title, lines[start + 1 : end]) for title, start, end in indexed_titles] def invocation_filter(lines: List[str]) -> List[str]: @@ -86,9 +83,7 @@ def invocation_filter(lines: List[str]) -> List[str]: ] -def annotate( - sections: List[Tuple[str, List[str]]] -) -> List[Tuple[Stage, List[str]]]: +def annotate(sections: List[Tuple[str, List[str]]]) -> List[Tuple[Stage, List[str]]]: """Annotate the stage output with pre-defined stage type. Annotate the sections with the pre-defined build stage that that @@ -101,8 +96,7 @@ def annotate( ordered and annotated build steps """ return [ - (ANNOTATION_MAP.get(title, Stage.UNKNOWN), lines) - for title, lines in sections + (ANNOTATION_MAP.get(title, Stage.UNKNOWN), lines) for title, lines in sections ] @@ -119,9 +113,7 @@ def parse(stdout: str) -> Dict[Stage, List[str]]: Return: """ raw_sections = sectioner(stdout) - sections = [ - (title, invocation_filter(lines)) for title, lines in raw_sections - ] + sections = [(title, invocation_filter(lines)) for title, lines in raw_sections] annotated = annotate(sections) # Unknown title detection and warnings @@ -138,7 +130,5 @@ def parse(stdout: str) -> Dict[Stage, List[str]]: ','.join(unknown_titles), ) build_stages = dict(annotated) - LOGGER.debug( - 'Detected build stages:\n\t%s', string_dictionary_of_list(build_stages) - ) + LOGGER.debug('Detected build stages:\n\t%s', string_dictionary_of_list(build_stages)) return build_stages diff --git a/src/arduino_cli_cmake_wrapper/types.py b/src/arduino_cli_cmake_wrapper/types.py index f4cf67d..34ba195 100644 --- a/src/arduino_cli_cmake_wrapper/types.py +++ b/src/arduino_cli_cmake_wrapper/types.py @@ -77,9 +77,7 @@ class MissingStageException(ArduinoCLIException): def __init__(self, stage: Stage): """Missing build stage.""" - super().__init__( - f'Failed to find any output for build stage: {stage.value}' - ) + super().__init__(f'Failed to find any output for build stage: {stage.value}') class MultipleInvocationException(ArduinoCLIException): diff --git a/src/arduino_cli_cmake_wrapper/util.py b/src/arduino_cli_cmake_wrapper/util.py index 5b57b7f..143379d 100644 --- a/src/arduino_cli_cmake_wrapper/util.py +++ b/src/arduino_cli_cmake_wrapper/util.py @@ -1,4 +1,5 @@ """Utility functions for the Arudino wrapper.""" + import re import shlex from pathlib import Path diff --git a/tox.ini b/tox.ini index 33eb8bc..96b5268 100644 --- a/tox.ini +++ b/tox.ini @@ -7,14 +7,14 @@ commands = python ci/bootstrap.py --no-env passenv = * -; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist +; a generative tox configuration, see: https://tox.wiki/en/latest/user_guide.html#generative-environments [tox] envlist = clean, check, docs, - {py38,py39,py310,py311,pypy38,pypy39}, + {py38,py39,py310,py311,py312,pypy38,pypy39,pypy310}, report ignore_basepython_conflict = true @@ -22,10 +22,12 @@ ignore_basepython_conflict = true basepython = pypy38: {env:TOXPYTHON:pypy3.8} pypy39: {env:TOXPYTHON:pypy3.9} + pypy310: {env:TOXPYTHON:pypy3.10} py38: {env:TOXPYTHON:python3.8} py39: {env:TOXPYTHON:python3.9} py310: {env:TOXPYTHON:python3.10} py311: {env:TOXPYTHON:python3.11} + py312: {env:TOXPYTHON:python3.12} {bootstrap,clean,check,report,docs}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests @@ -37,24 +39,25 @@ deps = pytest pytest-cov commands = - {posargs:pytest --cov --cov-report=term-missing -vv tests} + {posargs:pytest --cov --cov-report=term-missing --cov-report=xml -vv tests} [testenv:check] deps = docutils - check-manifest pre-commit readme-renderer pygments isort + setuptools-scm skip_install = true commands = python setup.py check --strict --metadata --restructuredtext - check-manifest . pre-commit run --all-files --show-diff-on-failure [testenv:docs] usedevelop = true +install_command = + python -m pip install --no-use-pep517 {opts} {packages} deps = -r{toxinidir}/docs/requirements.txt commands = @@ -70,7 +73,10 @@ commands = coverage html [testenv:clean] -commands = coverage erase +commands = + python setup.py clean + coverage erase skip_install = true deps = + setuptools coverage