diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index daba8580..f00a0fcc 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,4 +1,4 @@ -name: Setup base (python, pip cache, tox) +name: Setup base (python, uv, tox) inputs: python: description: "Python version to use" @@ -11,41 +11,17 @@ outputs: runs: using: "composite" steps: - - name: pip cache - uses: actions/cache@v4 - with: - path: | - ~/.cache/pip - key: ${{ runner.os }}-pip-${{ inputs.python }} - - - name: Cargo cache - uses: actions/cache/@v4 - with: - path: "~/.cargo" - key: ${{ runner.os }}-cargo - - - name: Poetry cache - uses: actions/cache/@v4 - with: - path: "~/.cache/pypoetry" - key: ${{ runner.os }}-poetry-${{ inputs.python }} - restore-keys: | - ${{ runner.os }}-poetry- - - uses: actions/setup-python@v6 id: python with: python-version: ${{ inputs.python }} - - name: upgrade pip and install tox + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Install tox and tox-uv shell: bash run: | - python -m pip -q install --upgrade pip "setuptools==65.6.2" - pip -q install "tox<4" tox-gh-actions - - - name: install Rust and Poetry - shell: bash - run : | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --profile minimal - source "$HOME/.cargo/env" - pip -q install poetry>=1.2.0 + uv tool install tox --with tox-uv --with tox-gh-actions diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2928b1af..2330164d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,7 +70,10 @@ jobs: steps: - uses: actions/checkout@v5 - uses: ./.github/actions/setup-semantic-release # node+semantic-release - - uses: ./.github/actions/setup # poetry + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true - id: semantic-release # branch policies defined in .releaserc env: GIT_AUTHOR_NAME: appland-release diff --git a/.gitignore b/.gitignore index 4473e002..fac985bc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .tool-versions poetry.lock +uv.lock __pycache__/ *.py[cod] @@ -22,3 +23,4 @@ htmlcov/ /ruff.toml appmap.log +*.sqlite3 diff --git a/.releaserc.yml b/.releaserc.yml index b5efcfc6..47b2e100 100644 --- a/.releaserc.yml +++ b/.releaserc.yml @@ -42,7 +42,7 @@ plugins: - pyproject.toml - - '@semantic-release/exec' - prepareCmd: | - /bin/bash ./ci/scripts/build_with_poetry.sh + /bin/bash ./ci/scripts/build_with_uv.sh - - '@semantic-release/github': - assets: - dist/*.whl diff --git a/README.md b/README.md index a73aa470..bf981d9a 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,15 @@ oldest version currently supported (see the ## Dependency management -[poetry](https://https://python-poetry.org/) for dependency management: +[uv](https://docs.astral.sh/uv/) is used for dependency management and provides fast package installation: -``` -% brew install poetry -% cd appmap-python -% poetry install +```bash +# Install uv (macOS/Linux) +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install dependencies +cd appmap-python +uv sync --all-extras ``` ### wrapt @@ -69,64 +72,63 @@ To update `wrapt`, use `tox` (described below) to run the `vendoring` environmen ## Linting [pylint](https://www.pylint.org/) for linting: -``` -% cd appmap-python -% poetry run pylint appmap +```bash +cd appmap-python +uv run tox -e lint + +# Or run pylint directly +uv run pylint appmap -------------------------------------------------------------------- Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00) - ``` -[Note that the current configuration has a threshold set which must be met for the Travis build to -pass. To make this easier to achieve, a number of checks have both been disabled. They should be -reenabled as soon as possible.] - ## Testing ### pytest -Note that you must install the dependencies contained in -[requirements-dev.txt](requirements-dev.txt) before running tests. See the explanation in -[pyproject.toml](pyproject.toml) for details. +[pytest](https://docs.pytest.org/en/stable/) for testing: -Additionally, the tests currently require that you set `APPMAP=true` and -`APPMAP_DISPLAY_PARAMS=true`. +```bash +cd appmap-python -[pytest](https://docs.pytest.org/en/stable/) for testing: +# Run all tests +APPMAP_DISPLAY_PARAMS=true uv run appmap-python pytest -``` -% cd appmap-python -% pip install -r requirements-test.txt -% APPMAP=true APPMAP_DISPLAY_PARAMS=true poetry run pytest +# Run tests with a specific Python version +APPMAP_DISPLAY_PARAMS=true uv run --python 3.9 appmap-python pytest + +# Run tests in parallel +APPMAP_DISPLAY_PARAMS=true uv run appmap-python pytest -n auto ``` ### tox -Additionally, the `tox` configuration provides the ability to run the tests for all -supported versions of Python and Django. +The `tox` configuration provides the ability to run the tests for all supported versions of Python and web frameworks (Django, Flask, SQLAlchemy). -`tox` requires that all the correct versions of Python to be available to create -the test environments. [pyenv](https://github.com/pyenv/pyenv) is an easy way to manage -multiple versions of Python, and the [xxenv-latest -plugin](https://github.com/momo-lab/xxenv-latest) can help get all the latest versions. +With `uv`, you don't need to pre-install Python versions - `uv` will automatically download and manage them: +```bash +cd appmap-python +# Run full test matrix (all Python versions and frameworks) +uv run tox -```sh -% brew install pyenv -% git clone https://github.com/momo-lab/xxenv-latest.git "$(pyenv root)"/plugins/xxenv-latest -% cd appmap-python -% pyenv latest local 3.{9,6,7,8} -% for v in 3.{9,6,7,8}; do pyenv latest install $v; done -% poetry run tox +# Run tests for a specific Python version +uv run tox -e py312-web + +# Run tests for specific framework +uv run tox -e py312-django5 + +# Update vendored wrapt dependency +uv run tox -e vendoring sync ``` ## Code Coverage [coverage](https://coverage.readthedocs.io/) for coverage: -``` -% cd appmap-python -% poetry run coverage run -m pytest -% poetry run coverage html -% open htmlcov/index.html +```bash +cd appmap-python +uv run coverage run -m pytest +uv run coverage html +open htmlcov/index.html ``` diff --git a/ci/scripts/build_with_poetry.sh b/ci/scripts/build_with_uv.sh similarity index 81% rename from ci/scripts/build_with_poetry.sh rename to ci/scripts/build_with_uv.sh index 53c89791..aa85b485 100755 --- a/ci/scripts/build_with_poetry.sh +++ b/ci/scripts/build_with_uv.sh @@ -4,16 +4,16 @@ set -e set -o pipefail if [ -z "$DISTRIBUTION_NAME" ] || [ "$DISTRIBUTION_NAME" = "appmap" ] ; then - exec poetry build $* + exec uv build $* fi -echo "Altering distribution name to $DISTRIBUTION_NAME" +echo "Altering distribution name to $DISTRIBUTION_NAME" cp -v pyproject.toml /tmp/pyproject.bak sed -i -e "s/^name = \".*\"/name = \"${DISTRIBUTION_NAME}\"/" pyproject.toml grep -n 'name = "' pyproject.toml -poetry build $* +uv build $* echo "Not patching artifacts with Provides-Dist, they won't work anyway (this flow is solely for publishing test)" cp -v /tmp/pyproject.bak pyproject.toml diff --git a/pylintrc b/pylintrc index 808bf36e..e281a970 100644 --- a/pylintrc +++ b/pylintrc @@ -95,7 +95,6 @@ recursive=no # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. -suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. diff --git a/pyproject.toml b/pyproject.toml index e0b54baf..e19a8cef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,101 +1,114 @@ -[tool.poetry] +[project] name = "appmap" version = "2.1.8" description = "Create AppMap files by recording a Python application." readme = "README.md" +requires-python = ">=3.8" +license = { text = "MIT" } authors = [ - "Alan Potter ", - "Viraj Kanwade ", - "Rafał Rzepecki " + { name = "Alan Potter", email = "alan@app.land" }, + { name = "Viraj Kanwade", email = "viraj.kanwade@forgeahead.io" }, + { name = "Rafał Rzepecki", email = "rafal@app.land" } ] -homepage = "https://github.com/applandinc/appmap-python" -license = "MIT" classifiers = [ - 'Development Status :: 4 - Beta', - 'Framework :: Django', - 'Framework :: Django :: 3.2', - 'Framework :: Flask', - 'Framework :: Pytest', - 'Intended Audience :: Developers', - 'Topic :: Software Development', - 'Topic :: Software Development :: Debuggers', - 'Topic :: Software Development :: Documentation' + "Development Status :: 4 - Beta", + "Framework :: Django", + "Framework :: Django :: 3.2", + "Framework :: Flask", + "Framework :: Pytest", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Topic :: Software Development", + "Topic :: Software Development :: Debuggers", + "Topic :: Software Development :: Documentation", ] -include = [ - { path = 'appmap.pth', format = ['sdist','wheel'] }, - { path = '_appmap/test/**/*', format = 'sdist' } -] - -exclude = ['_appmap/wrapt'] - -packages = [ - { include = "appmap" }, - { include = "_appmap/*.py" }, - { include = "_appmap/wrapt/**/*", from = "vendor" } -] - -[tool.poetry.dependencies] # Please update the documentation if changing the supported python version # https://github.com/applandinc/applandinc.github.io/blob/master/_docs/reference/appmap-python.md#supported-versions -python = "^3.8" -PyYAML = ">=5.3.0" -inflection = ">=0.3.0" -importlib-resources = "^5.4.0" -packaging = ">=19.0" -# If you include "Django" as an optional dependency here, you'll be able to use poetry to install it -# in your dev environment. However, doing so causes poetry v1.2.0 to remove it from the virtualenv -# *created and managed by tox*, i.e. not your dev environment. -# -# So, if you'd like to run the tests outside of tox, run `pip install -r requirements-dev.txt` to -# install it and the rest of the dev dependencies. +dependencies = [ + "PyYAML>=5.3.0", + "inflection>=0.3.0", + "importlib-resources>=5.4.0", + "packaging>=19.0", +] -[tool.poetry.group.dev.dependencies] -Twisted = "^22.4.0" -incremental = "<24.7.0" -asgiref = "^3.7.2" -black = "^24.2.0" -coverage = "^5.3" -flake8 = "^3.8.4" -httpretty = "^1.0.5" -isort = "^5.10.1" -pprintpp = ">=0.4.0" -pyfakefs = "^5.3.5" -pylint = "^3.0" -pytest = "^7.3.2" -pytest-django = "~4.7" -pytest-mock = "^3.5.1" -pytest-randomly = "^3.5.0" -pytest-shell-utilities = "^1.8.0" -pytest-xprocess = "^0.23.0" -python-decouple = "^3.5" -requests = "^2.25.1" -tox = "^3.22.0" -# v2.30.0 of "requests" depends on urllib3 v2, which breaks the tests for http_client_requests. Pin -# to v1 until this gets fixed. -urllib3 = "^1" -uvicorn = "^0.27.1" -fastapi = "^0.110.0" -httpx = "^0.27.0" -pytest-env = "^1.1.3" -pytest-console-scripts = "^1.4.1" -pytest-xdist = "^3.6.1" -psutil = "^6.0.0" -ruff = "^0.5.3" +[project.optional-dependencies] +test = [ + "pytest>=7.3.2,<8.0", + "pytest-mock>=3.5.1,<4.0", + "pytest-randomly>=3.5.0,<4.0", + "pytest-shell-utilities>=1.8.0,<2.0", + "pytest-xprocess>=0.23.0,<1.0", + "pytest-env>=1.1.3,<2.0", + "pytest-console-scripts>=1.4.1,<2.0", + "pytest-xdist>=3.6.1,<4.0", + "httpretty>=1.0.5,<2.0", + "pyfakefs>=5.3.5,<6.0", + "requests>=2.25.1,<3.0", + "python-decouple>=3.5,<4.0", + "Twisted>=22.4.0,<23.0", + "incremental<24.7.0", + "asgiref>=3.7.2,<4.0", + "psutil>=6.0.0,<7.0", + "uvicorn>=0.27.1,<1.0", + "fastapi>=0.110.0,<1.0", + "httpx>=0.27.0,<1.0", + # v2.30.0 of "requests" depends on urllib3 v2, which breaks the tests for http_client_requests. Pin + # to v1 until this gets fixed. + "urllib3>=1,<2", + "Django>=4.0,<5.0", + "Flask>=3.0", + "sqlalchemy>=2.0,<3.0", + "pytest-django>=4.7,<5.0", + "numpy>=1.24.4,<2.0; python_version < '3.9'", + "numpy>=2.0; python_version >= '3.9'", +] +dev = [ + "appmap[test]", + "black>=24.2.0,<25.0", + "coverage>=5.3,<6.0", + "flake8>=3.8.4,<4.0", + "isort>=5.10.1,<6.0", + "pprintpp>=0.4.0,<1.0", + "pylint>=3.0,<4.0", + "tox>=4.0,<5.0", + "tox-uv>=1.0,<2.0", + "tox-gh-actions>=3.0,<4.0", + "ruff>=0.5.3,<1.0", +] -[build-system] -requires = ["poetry-core>=1.1.0"] -build-backend = "poetry.core.masonry.api" +[project.urls] +Homepage = "https://github.com/applandinc/appmap-python" -[tool.poetry.plugins."pytest11"] -appmap = "appmap.pytest" - -[tool.poetry.scripts] +[project.scripts] appmap-agent-init = "appmap.command.appmap_agent_init:run" appmap-agent-status = "appmap.command.appmap_agent_status:run" appmap-agent-validate = "appmap.command.appmap_agent_validate:run" appmap-python = "appmap.command.runner:run" +[project.entry-points.pytest11] +appmap = "appmap.pytest" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["appmap", "_appmap"] +exclude = ["_appmap/test", "_appmap/wrapt"] +force-include = { "appmap.pth" = "appmap.pth", "vendor/_appmap/wrapt" = "_appmap/wrapt" } + +[tool.hatch.build.targets.sdist] +only-include = [ + "appmap", + "_appmap", + "appmap.pth", + "vendor", + "pyproject.toml", + "README.md", + "LICENSE", +] + [tool.black] line-length = 102 extend-exclude = ''' diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index f70988fa..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,10 +0,0 @@ -#requirements-dev.txt -tox -django -flask >=2, <= 3 -pytest-django<4.8 -fastapi -httpx -sqlalchemy -debugpy -numpy \ No newline at end of file diff --git a/requirements-test.txt b/requirements-test.txt deleted file mode 100644 index 16b853d6..00000000 --- a/requirements-test.txt +++ /dev/null @@ -1,3 +0,0 @@ -django ~= 3.2 -pytest-django < 4.8 -sqlalchemy < 2.0 diff --git a/tool-versions.example b/tool-versions.example deleted file mode 100644 index 4a4d044f..00000000 --- a/tool-versions.example +++ /dev/null @@ -1 +0,0 @@ -python 3.8.18 3.9.18 3.10.13 3.11.7 3.12.1 diff --git a/tox.ini b/tox.ini index be81c9d2..2b8ed82f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,8 @@ [tox] +requires = + tox>=4 + tox-uv + tox-gh-actions isolated_build = true # The *-web environments test the latest versions of Django and Flask with the full test suite. For @@ -22,13 +26,15 @@ deps= sqlalchemy >=2.0, <3.0 [testenv] -passenv = +usedevelop = true +extras = test +passenv = PYTEST_XDIST_AUTO_NUM_WORKERS -setenv = +setenv = APPMAP_DISPLAY_PARAMS=true deps= - poetry web: {[web-deps]deps} + web,django3,django4,django5: pytest-django >=4.7, <5.0 py38: numpy==1.24.4 py3{9,10,11,12}: numpy >=2 flask2: Flask >= 2.0, <3.0 @@ -38,31 +44,30 @@ deps= sqlalchemy1: sqlalchemy >=1.4.11, <2.0 commands = - poetry install -v - web: poetry run appmap-python {posargs:pytest -n logical} - django3: poetry run appmap-python pytest -n logical _appmap/test/test_django.py - django4: poetry run appmap-python pytest -n logical _appmap/test/test_django.py - django5: poetry run appmap-python pytest -n logical _appmap/test/test_django.py - flask2: poetry run appmap-python pytest -n logical _appmap/test/test_flask.py - sqlalchemy1: poetry run appmap-python pytest -n logical _appmap/test/test_sqlalchemy.py + web: appmap-python {posargs:pytest -n logical} + django3: appmap-python pytest -n logical _appmap/test/test_django.py + django4: appmap-python pytest -n logical _appmap/test/test_django.py + django5: appmap-python pytest -n logical _appmap/test/test_django.py + flask2: appmap-python pytest -n logical _appmap/test/test_flask.py + sqlalchemy1: appmap-python pytest -n logical _appmap/test/test_sqlalchemy.py [testenv:lint] -skip_install = True +skip_install = False +extras = test deps = - poetry {[web-deps]deps} numpy >=2 + pylint >=3.0 commands = - poetry install # It doesn't seem great to disable cyclic-import checking, but the imports # aren't currently causing any problems. They should probably get fixed # sometime soon. - {posargs:poetry run pylint --disable=cyclic-import -j 0 appmap _appmap} + {posargs:pylint --disable=cyclic-import -j 0 appmap _appmap} [testenv:vendoring] skip_install = True deps = vendoring commands = - poetry run vendoring {posargs:sync} + vendoring {posargs:sync} # We don't need the .pyi files vendoring generates python -c 'from pathlib import Path; all(map(Path.unlink, Path("vendor").rglob("*.pyi")))'