diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0163c07e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# Set update schedule for GitHub Actions +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + + # Maintain submodule versions + # NOTE: too noisy, easier to update by hand + #- package-ecosystem: "gitsubmodule" + # directory: "/" + # schedule: + # interval: "monthly" + + # Maintain dependencies for pip/poetry + # NOTE: too noisy, easier to update by hand + #- package-ecosystem: "pip" + # directory: "/" + # schedule: + # interval: "monthly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 416a0cf6..ffcd87b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,14 +16,19 @@ jobs: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} env: + # Wether to build and include extras (like aw-notify and aw-watcher-input) + AW_EXTRAS: true # sets the macOS version target, see: https://users.rust-lang.org/t/compile-rust-binary-for-older-versions-of-mac-osx/38695 MACOSX_DEPLOYMENT_TARGET: 10.9 + defaults: + run: + shell: bash strategy: fail-fast: false matrix: os: [ubuntu-20.04, windows-latest, macOS-12] python_version: [3.9] - node_version: [16] + node_version: [20] skip_rust: [false] skip_webui: [false] experimental: [false] @@ -31,11 +36,11 @@ jobs: #include: # - os: ubuntu-latest # python_version: 3.9 - # node_version: 16 + # node_version: 20 # experimental: true - steps: - - uses: actions/checkout@v2 + steps: + - uses: actions/checkout@v4 with: submodules: 'recursive' fetch-depth: 0 # fetch all branches and tags @@ -49,7 +54,7 @@ jobs: - name: Set up Python if: runner.os != 'macOS' - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} @@ -58,6 +63,14 @@ jobs: if: runner.os == 'macOS' run: | curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" + + # Python 3.11+ only has *macos11.pkg, so no more *macosx10.9.pkg + # the 'macos11' naming seems to suggest it only supports macos11 and up, + # but the release page says "for macOS 10.9 and later", + # unclear what the resulting binary compatibility will be. + # + # curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o "python.pkg" + sudo installer -pkg python.pkg -target / echo "/Library/Frameworks/Python.framework/Versions/${{ matrix.python_version }}/bin" >> $GITHUB_PATH "/Applications/Python ${{ matrix.python_version }}/Install Certificates.command" @@ -67,25 +80,23 @@ jobs: - name: Set up Node if: ${{ !matrix.skip_webui }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} - - name: Set up Rust nightly + - name: Set up Rust if: ${{ !matrix.skip_rust }} - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master id: toolchain with: - profile: minimal - toolchain: nightly - override: true + toolchain: stable - name: Get npm cache dir id: npm-cache-dir run: | - echo "::set-output name=dir::$(npm config get cache)" + echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - uses: actions/cache@v1 + - uses: actions/cache@v4 name: Cache npm if: ${{ !matrix.skip_webui }} env: @@ -97,19 +108,18 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache cargo build - uses: actions/cache@v1 + uses: actions/cache@v4 if: ${{ !matrix.skip_rust && (runner.os != 'macOS') }} # cache doesn't seem to behave nicely on macOS, see: https://github.com/ActivityWatch/aw-server-rust/issues/180 env: cache-name: cargo-build-target with: path: aw-server-rust/target # key needs to contain rustc_hash due to https://github.com/ActivityWatch/aw-server-rust/issues/180 - key: ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}- - name: Install APT dependencies - shell: bash if: runner.os == 'Linux' run: | sudo apt-get update @@ -133,23 +143,21 @@ jobs: libxrender-dev - name: Install dependencies - shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then choco install innosetup fi - pip3 install poetry virtualenv + pip3 install poetry==1.3.2 - name: Build - shell: bash run: | - python3 -m virtualenv venv + python3 -m venv venv source venv/bin/activate || source venv/Scripts/activate poetry install make build SKIP_WEBUI=${{ matrix.skip_webui }} SKIP_SERVER_RUST=${{ matrix.skip_rust }} + pip freeze # output Python packages, useful for debugging dependency versions - name: Run tests - shell: bash run: | source venv/bin/activate || source venv/Scripts/activate make test SKIP_SERVER_RUST=${{ matrix.skip_rust }} @@ -162,7 +170,6 @@ jobs: make test-integration - name: Package - shell: bash run: | source venv/bin/activate || source venv/Scripts/activate poetry install # run again to ensure we have the correct version of PyInstaller @@ -201,7 +208,7 @@ jobs: APPLE_TEAMID: ${{ secrets.APPLE_TEAMID }} CERTIFICATE_MACOS_P12_BASE64: ${{ secrets.CERTIFICATE_MACOS_P12_BASE64 }} CERTIFICATE_MACOS_P12_PASSWORD: ${{ secrets.CERTIFICATE_MACOS_P12_PASSWORD }} - + - name: Package AppImage if: startsWith(runner.os, 'linux') run: | @@ -214,7 +221,7 @@ jobs: ./scripts/package/package-deb.sh - name: Upload packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: builds-${{ runner.os }}-py${{ matrix.python_version }} path: dist/activitywatch-*.* @@ -223,12 +230,12 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') # only on runs triggered from tag steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: 'recursive' fetch-depth: 0 # fetch all branches and tags - - uses: nowsprinting/check-version-format-action@v2 + - uses: ActivityWatch/check-version-format-action@v2 id: version with: prefix: 'v' @@ -238,7 +245,7 @@ jobs: echo "${{ steps.version.outputs.full }} (stable: ${{ steps.version.outputs.is_stable }})" - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: "3.9" @@ -257,19 +264,19 @@ jobs: mv changelog.md release_notes.md - name: Upload release notes - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release_notes path: release_notes.md release: needs: [build, release-notes] - if: startsWith(github.ref, 'refs/tags/v') # only on runs triggered from tag + if: startsWith(github.ref, 'refs/tags/v') # only run on tag runs-on: ubuntu-latest steps: # Will download all artifacts to path - name: Download build artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: dist @@ -278,7 +285,7 @@ jobs: working-directory: dist # detect if version tag is stable/beta - - uses: nowsprinting/check-version-format-action@v2 + - uses: ActivityWatch/check-version-format-action@v2 id: version with: prefix: 'v' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9f565039..bd663a80 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml new file mode 100644 index 00000000..6a5ea1c4 --- /dev/null +++ b/.github/workflows/dependabot-automerge.yml @@ -0,0 +1,27 @@ +name: Dependabot Auto-merge + +# NOTE: This workflow relies on a Personal Access Token from the @ActivityWatchBot user +# See this issue for details: https://github.com/ridedott/merge-me-action/issues/1581 + +on: + workflow_run: + types: + - completed + workflows: + # List all required workflow names here. + - Build + +permissions: + contents: write + pull-requests: read + +jobs: + auto_merge: + name: Auto-merge + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' && github.actor == 'dependabot[bot]' + + steps: + - uses: ridedott/merge-me-action@v2 + with: + GITHUB_TOKEN: ${{ secrets.AWBOT_GH_TOKEN }} diff --git a/.github/workflows/diagram.yml b/.github/workflows/diagram.yml index 5fc5ad7c..7cadd74d 100644 --- a/.github/workflows/diagram.yml +++ b/.github/workflows/diagram.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive - name: Also checkout docs & website diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..8f5cc9b2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,175 @@ +name: Test + +on: + #push: + # branches: [ master ] + #pull_request: + # branches: [ master ] + workflow_dispatch: + + + +jobs: + # an integration test designed to catch bugs triggered by updating (database migrations and such) + upgrades: + name: upgrade from ${{ matrix.aw_server_old }} ${{ matrix.aw_server_old_args }} to ${{ matrix.aw_server_new }} ${{ matrix.aw_server_new_args }} + # needs: [build] + #if: startsWith(github.ref, 'refs/tags/v') # only run on tag + runs-on: ubuntu-latest + env: + old_version: 'v0.12.2' + new_version: 'v0.12.3b3' + strategy: + fail-fast: false + matrix: + aw_server_old: ['aw-server', 'aw-server-rust'] + aw_server_new: ['aw-server', 'aw-server-rust'] + aw_server_old_args: [''] + aw_server_new_args: [''] + include: + # python, peewee (default) + - aw_server_old: 'aw-server' + aw_server_new: 'aw-server' + # python, sqlite + # FIXME: sqlite broken since aw-server enabled flask multithreading (new default) + - aw_server_old: "aw-server" + aw_server_new: "aw-server" + aw_server_old_args: "--storage sqlite" + aw_server_new_args: "--storage sqlite" + old_version: 'v0.12.2' + new_version: 'v0.12.3b3' + # python, peewee to sqlite + # FIXME: broken, same thing with sqlite as above + - aw_server_old: "aw-server" + aw_server_new: "aw-server" + aw_server_old_args: "--storage peewee" + aw_server_new_args: "--storage sqlite" + old_version: 'v0.12.2' + new_version: 'v0.12.3b3' + exclude: + # rust to python, not supported + - aw_server_old: 'aw-server-rust' + aw_server_new: 'aw-server' + + steps: + # Will download all artifacts to path + - name: Download build artifacts + if: ${{ env.new_version == 'this' }} + uses: actions/download-artifact@v4 + with: + name: builds-Linux-py3.9 + path: dist + + # Only used during testing, so we don't have to wait for the main build job + - name: Download new ActivityWatch + if: ${{ env.new_version != 'this' }} + run: | + mkdir dist + pushd dist + wget -q https://github.com/ActivityWatch/activitywatch/releases/download/${{ env.new_version }}/activitywatch-${{ env.new_version }}-linux-x86_64.zip + + - name: Install new & old ActivityWatch + run: | + pushd dist + + # New version + unzip activitywatch-*-linux-x86_64.zip + mv activitywatch/ aw-new + + # Old version + wget -q -O aw-old.zip https://github.com/ActivityWatch/activitywatch/releases/download/${{ env.old_version }}/activitywatch-${{ env.old_version }}-linux-x86_64.zip + unzip aw-old.zip + mv activitywatch/ aw-old + + - name: Display structure of downloaded files + run: ls -R + working-directory: dist + + - name: Run and test old server + run: | + bin=dist/aw-old/${{ matrix.aw_server_old }}/${{ matrix.aw_server_old }} + url="http://localhost:5600" + + # Check version + $bin --version || true # due to bug in old aw-server + + # Run server and log output + $bin ${{ matrix.aw_server_old_args }} >> log-old.txt 2>&1 & + sleep 5 # wait for startup + + # Set server URL + + # Get server info + curl "$url/api/0/info" --fail-with-body + + # Create bucket + curl -X 'POST' --fail-with-body \ + "$url/api/0/buckets/aw-test" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "client": "test", + "type": "test", + "hostname": "test" + }' + + # Get buckets + curl "$url/api/0/buckets/" -H 'accept: application/json' + + # Send a heartbeat + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + curl -X 'POST' \ + "$url/api/0/buckets/aw-test/heartbeat?pulsetime=0" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "timestamp": "'$timestamp'", + "duration": 0, + "data": {"key": "test value"} + }' + + # Give a sec, then kill server process + sleep 1 + kill $! + + - name: Run and test new server + run: | + bin=dist/aw-new/${{ matrix.aw_server_new }}/${{ matrix.aw_server_new }} + url="http://localhost:5600" + + # Check version + $bin --version + + # Run server and log output + $bin ${{ matrix.aw_server_new_args }} >> log-new.txt 2>&1 & + sleep 5 # wait for startup + + # Get server info + curl "$url/api/0/info" + + # Get buckets + curl "$url/api/0/buckets/" -H 'accept: application/json' + + # Send a heartbeat + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + curl -X 'POST' --fail-with-body \ + "$url/api/0/buckets/aw-test/heartbeat?pulsetime=60" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "timestamp": "'$timestamp'", + "duration": 0, + "data": {"key": "test value"} + }' + + # Give a sec, then kill server process + sleep 1 + kill $! + + - name: Output logs + if: always() + run: | + cat log-old.txt || true + echo "\n---\n" + cat log-new.txt || true + diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index d6d71dd6..b76c8eda 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -6,7 +6,7 @@ jobs: publish: runs-on: windows-latest # action can only be run on windows steps: - - uses: vedantmgoyal2009/winget-releaser@v1 + - uses: vedantmgoyal2009/winget-releaser@v2 with: identifier: ActivityWatch.ActivityWatch token: ${{ secrets.GH_TOKEN_WINGET_AUTOUPDATE }} diff --git a/.gitmodules b/.gitmodules index 2bb7019c..8e7d1bda 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,9 @@ [submodule "aw-server-rust"] path = aw-server-rust url = https://github.com/ActivityWatch/aw-server-rust.git +[submodule "aw-notify"] + path = aw-notify + url = https://github.com/ErikBjare/aw-notify.git +[submodule "aw-watcher-input"] + path = aw-watcher-input + url = https://github.com/ActivityWatch/aw-watcher-input.git diff --git a/.tool-versions b/.tool-versions index ac8cc40e..59417e9a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,7 +1,4 @@ -poetry 1.1.12 -nodejs 15.14.0 +poetry 1.5.1 +nodejs 16.20.2 rust nightly - -# in order for pyinstall to work properly: -# export PYTHON_CONFIGURE_OPTS="--enable-framework" -python 3.9.9 +python 3.9.13 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 09bbd33f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,116 +0,0 @@ -os: linux -dist: xenial -language: python -python: 3.7 - -env: - - TRAVIS_NODE_VERSION=12 - -services: - - mongodb - -cache: - directories: - - $HOME/.rustup - - $HOME/.cache/pip - - aw-server-rust/target - -jobs: - include: - - os: linux - python: 3.7 - env: - - DEPLOY=true - - TRAVIS_NODE_VERSION=12 - - os: osx - osx_image: xcode10.1 - language: generic - python: null - cache: - directories: - - $HOME/Library/Caches/Homebrew - env: - - DEPLOY=true - - TRAVIS_NODE_VERSION=12 - -before_install: - # macOS: - # - Install Python 3.7 - # - Install node using nvm - # - Set up a virtualenv - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - travis_wait 20 brew update; - brew unlink python; - brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/2efdfe5519df7654ece8d70786baa298e568eafd/Formula/python.rb; - brew link python; - brew install findutils; - export PATH="/usr/local/opt/findutils/libexec/gnubin:$PATH"; - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION; - fi - # Linux: - # - Install nvm and use correct node version - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - sudo apt-get install libqt5xml5; - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION; - fi - # Upgrade pip and install poetry - - python3 -m pip install --upgrade pip wheel poetry; - # Install Rust - - curl https://build.travis-ci.org/files/rustup-init.sh -sSf | sh -s -- -y --default-toolchain nightly - - export PATH="$HOME/.cargo/bin:$PATH"; - # Print version used - - python3 --version - - pip3 --version - - node --version - - npm --version - # Set up a virtualenv on macOS - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - pip3 install virtualenv; - virtualenv -p python3 ~/venv; - source ~/venv/bin/activate; - fi - -install: - - poetry install # installs dev dependencies - # Output something every 10 minutes or Travis kills the job if building takes more than 20min without output (as pip seems to on Windows when installing aw-server) - - while sleep 9m; do echo "=====[ $SECONDS seconds still running ]====="; done & - - make build - - kill %1 # Killing background sleep loop - - "make install" - -script: - - "make test" - - "make test-integration" - - "poetry install" # install pyinstaller again since it gets uninstalled for some reason - - "make package" - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - ./scripts/ci/import-macos-p12.sh - make dist/ActivityWatch.dmg; - mv dist/ActivityWatch.dmg dist/activitywatch-$(scripts/package/getversion.sh)-macos-x86_64.dmg; - fi - -before_deploy: - # Remove things in the dist folder that shouldn't be deployed (such as the folder from which the zip was created) - - find dist/ -type f | grep -v "activitywatch-[^/]*" | xargs --no-run-if-empty rm -r - -deploy: - - provider: releases - skip_cleanup: true - file_glob: true - file: dist/activitywatch-*.* - token: - secure: PSkaHT7bhmCBFutBhfIyHd/j784e0bgzVrahA9+IrEZQqHmJzGvkytf4mQ+EWz0lYDX/Jko7aIFfuCQ8yoVNnnwCu8SwTKXcwrkwuJrymupIK+Xxau6C4rMBLK81mphZs8t2YjJK720EquZ54Tt5Y79Dh2tk9ZxdgqWgVLVhhZziD2/I5BfpsqRgCFqZc4UKAOAf5jpe1lgCOz4zkLP3vgQUyIJgkc9A1BT01f5v860hAlH8HchBAlIMklzPJt3ps9ch8dLRtCC3ZQRWIQpNGjw10P4BO3DqjF4ASeo0ZtbLGIFZeWnnAbQxRIztPUqiYnTQckvHcHYAKG8cyO8SBKQj1au5FRTCHbafpOOzLDWkXx+fqaYakxmPlW6YGp+zaiDQsmNCIdCWWsFU8+gUgZL8Owf4eYc2LI4LW7SI/RC7HjllqNP8WVbZSbQRcvQrK9ZN6fiOixTLni2cXyKy6SxVaDd/+iji8YwPW8JrOawTsXIoZRDE6vq41Y47tO/MU9yLm8RkRkApBGgsc+5RJCEEiDyghJ3op+rWIavcMmSZQ3TBo5e00XyPBd7dGkxoLk9WUSuaZgTmi7FSfDDxWW9TiE7tP2ruIV4fcAlOOkM5ll3K6XIo61JM0JAuAKsg7g32lOnr2DEGDk8XlV3QqIEzwjXLjdVrk3ALabp8HjI= - on: - tags: true - condition: $DEPLOY = true - - provider: s3 - skip_cleanup: true - access_key_id: AKIAIISX5RJFA4X23TTA - secret_access_key: - secure: RMrCpX6HsOlRwU3DW8kycQZKvlMy12jGZ2PY6mtyjRAy4S8ddq1FA42KlHnvCRxqwjBp7WBbINoczAiNUfoT62oa8/csSTO2WkD2XxRfIEjrY/NSBrz4lQBnQ9RENez4DBr0HsLSUwWt1ySN1O7cs+RC2Cz/i9+5qpMzU1gQgZYYBB77p4GR/S6HHDIbMO7c9IjhOHQp3p7d4evyvZ9+Oq1uncgKdzl5Qnl/xov6w8F6Yi0Tpe1q1l5UftpQmq6k1PgKQPTIv61r2mWCEAW77LeJaQvJeKY/UfXPecWHS6SriFQqraAtbSokD7juYwiddQ2niJ3q2zLTQBTt5paA+0lYe9Vwv0wQqAThUCnpBfPGTmowTeyW1zkoPLLEGPiRzVlwO6cV5jaModn54Jp210k301SuxS8CG9QbGOLJPplsLWx/+SX225aggpUqYd6YLkqrb/ikPfmrUeaM25ctVz7QoMzM8VuurcVgkNLQKbZvHutVNM056vuZqTKBNUsHwS0CPYwL9R0+z0Kz+7Pm9XS0jtnDl7fUcBUD7EVgimweAodEDrjbweDbvZN+0kiGcSjlARwhIQ1X3zUPqb/R1SfXZOk1/Koe48UScnpWZEFItga1ftF+OfGX7mitzN5FcX7SfBUzSuu0C8zouYRct+zfmfnBbGkOy3igVaGg7oY= - bucket: activitywatch-builds - local_dir: dist - acl: public_read - on: - all_branches: true - condition: $DEPLOY = true diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..31dbb4b3 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,14 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it as below." +authors: +- family-names: "Bjäreholt" + given-names: "Erik" + orcid: "https://orcid.org/0000-0003-1350-9677" +- family-names: "Bjäreholt" + given-names: "Johan" + orcid: "https://orcid.org/0000-0003-4789-3160" +title: "ActivityWatch" +version: 0.12.1 +doi: 10.5281/zenodo.4957165 +date-released: 2022-09-26 +url: "https://github.com/ActivityWatch/activitywatch" diff --git a/Makefile b/Makefile index 0c6eada9..67d8fdd2 100644 --- a/Makefile +++ b/Makefile @@ -7,51 +7,55 @@ # # We recommend creating and activating a Python virtualenv before building. # Instructions on how to do this can be found in the guide linked above. - -# These targets should always rerun .PHONY: build install test clean clean_all SHELL := /usr/bin/env bash +SUBMODULES := aw-core aw-client aw-qt aw-server aw-server-rust aw-watcher-afk aw-watcher-window + +# Exclude aw-server-rust if SKIP_SERVER_RUST is true +ifeq ($(SKIP_SERVER_RUST),true) + SUBMODULES := $(filter-out aw-server-rust,$(SUBMODULES)) +endif +# Include extras if AW_EXTRAS is true +ifeq ($(AW_EXTRAS),true) + SUBMODULES := $(SUBMODULES) aw-notify aw-watcher-input +endif + +# A function that checks if a target exists in a Makefile +# Usage: $(call has_target,,) +define has_target +$(shell make -q -C $1 $2 >/dev/null 2>&1; if [ $$? -eq 0 -o $$? -eq 1 ]; then echo $1; fi) +endef + +# Submodules with test/package/lint/typecheck targets +TESTABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),test)) +PACKAGEABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),package)) +LINTABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),lint)) +TYPECHECKABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),typecheck)) + # The `build` target # ------------------ # # What it does: # - Installs all the Python modules # - Builds the web UI and bundles it with aw-server -# -# Tips: -# - Set the environment variable `PIP_USER=true` for pip to install all Python -# packages as user packages (same as `pip install --user `). This makes -# it possible to install without using a virtualenv (or root). -build: - if [ -e "aw-core/.git" ]; then \ - echo "Submodules seem to already be initialized, continuing..."; \ - else \ - git submodule update --init --recursive; \ - fi -# +build: aw-core/.git # needed due to https://github.com/pypa/setuptools/issues/1963 # would ordinarily be specified in pyproject.toml, but is not respected due to https://github.com/pypa/setuptools/issues/1963 pip install 'setuptools>49.1.1' -# - make --directory=aw-core build - make --directory=aw-client build - make --directory=aw-watcher-afk build - make --directory=aw-watcher-window build - make --directory=aw-server build SKIP_WEBUI=$(SKIP_WEBUI) -ifeq ($(SKIP_SERVER_RUST),true) # Skip building aw-server-rust if SKIP_SERVER_RUST is true - @echo "Skipping aw-server-rust build" -else - @echo 'Looking for rust...' - @if (which cargo); then \ - echo 'Rust found!'; \ - make --directory=aw-server-rust build SKIP_WEBUI=$(SKIP_WEBUI); \ - else \ - echo 'Rust not found, skipping aw-server-rust!'; \ + @if [ "$(SKIP_SERVER_RUST)" = "false" ]; then \ + if (which cargo); then \ + echo 'Rust found!'; \ + else \ + echo 'ERROR: Rust not found, try running with SKIP_SERVER_RUST=true'; \ + exit 1; \ + fi \ fi -endif - make --directory=aw-qt build + for module in $(SUBMODULES); do \ + echo "Building $$module"; \ + make --directory=$$module build SKIP_WEBUI=$(SKIP_WEBUI) || { echo "Error in $$module build"; exit 2; }; \ + done # The below is needed due to: https://github.com/ActivityWatch/activitywatch/issues/173 make --directory=aw-client build make --directory=aw-core build @@ -81,16 +85,16 @@ update: lint: - pylint -E \ - aw-core/aw_core/ \ - aw-core/aw_datastore/ \ - aw-core/aw_transform/ \ - aw-core/aw_analysis/ \ - aw-client/aw_client/ \ - aw-server/aw_server/ \ - aw-watcher-window/aw_watcher_window/ \ - aw-watcher-afk/aw_watcher_afk/ \ - aw-qt/aw_qt/ + @for module in $(LINTABLES); do \ + echo "Linting $$module"; \ + make --directory=$$module lint || { echo "Error in $$module lint"; exit 2; }; \ + done + +typecheck: + @for module in $(TYPECHECKABLES); do \ + echo "Typechecking $$module"; \ + make --directory=$$module typecheck || { echo "Error in $$module typecheck"; exit 2; }; \ + done # Uninstall # --------- @@ -104,21 +108,25 @@ uninstall: done test: - make --directory=aw-core test - make --directory=aw-client test - make --directory=aw-server test - make --directory=aw-qt test -ifeq ($(SKIP_SERVER_RUST),true) # Skip testing aw-server-rust if SKIP_SERVER_RUST is true - @echo "Skipping aw-server-rust test" -else - make --directory=aw-server-rust test -endif + @for module in $(TESTABLES); do \ + echo "Running tests for $$module"; \ + poetry run make -C $$module test || { echo "Error in $$module tests"; exit 2; }; \ + done test-integration: # TODO: Move "integration tests" to aw-client # FIXME: For whatever reason the script stalls on Appveyor # Example: https://ci.appveyor.com/project/ErikBjare/activitywatch/build/1.0.167/job/k1ulexsc5ar5uv4v - pytest ./scripts/tests/integration_tests.py ./aw-server/tests/ -v + # aw-server-python + @echo "== Integration testing aw-server ==" + @pytest ./scripts/tests/integration_tests.py ./aw-server/tests/ -v + # aw-server-rust + @echo "== Integration testing aw-server-rust ==" + @export PATH=aw-server-rust/target/release:aw-server-rust/target/debug:${PATH}; \ + pytest ./scripts/tests/integration_tests.py ./aw-server/tests/ -v + +%/.git: + git submodule update --init --recursive ICON := "aw-qt/media/logo/logo.png" @@ -150,25 +158,16 @@ dist/notarize: ./scripts/notarize.sh package: + rm -rf dist mkdir -p dist/activitywatch -# - make --directory=aw-watcher-afk package - cp -r aw-watcher-afk/dist/aw-watcher-afk dist/activitywatch -# - make --directory=aw-watcher-window package - cp -r aw-watcher-window/dist/aw-watcher-window dist/activitywatch -# - make --directory=aw-server package - cp -r aw-server/dist/aw-server dist/activitywatch -ifeq ($(SKIP_SERVER_RUST),true) - @echo "Skipping aw-server-rust package" -else - make --directory=aw-server-rust package - mkdir -p dist/activitywatch/aw-server-rust - cp -r aw-server-rust/target/package/* dist/activitywatch/aw-server-rust -endif - make --directory=aw-qt package - cp -r aw-qt/dist/aw-qt/. dist/activitywatch + for dir in $(PACKAGEABLES); do \ + make --directory=$$dir package; \ + cp -r $$dir/dist/$$dir dist/activitywatch; \ + done +# Move aw-qt to the root of the dist folder + mv dist/activitywatch/aw-qt aw-qt-tmp + mv aw-qt-tmp/* dist/activitywatch + rmdir aw-qt-tmp # Remove problem-causing binaries rm -f dist/activitywatch/libdrm.so.2 # see: https://github.com/ActivityWatch/activitywatch/issues/161 rm -f dist/activitywatch/libharfbuzz.so.0 # see: https://github.com/ActivityWatch/activitywatch/issues/660#issuecomment-959889230 @@ -187,13 +186,9 @@ clean: # Clean all subprojects clean_all: clean - make --directory=aw-client clean - make --directory=aw-core clean - make --directory=aw-qt clean - make --directory=aw-server clean - make --directory=aw-watcher-afk clean - make --directory=aw-watcher-window clean - make --directory=aw-server-rust clean + for dir in $(SUBMODULES); do \ + make --directory=$$dir clean; \ + done clean-auto: rm -rIv **/aw-server-rust/target diff --git a/README.md b/README.md index efbe37da..92ec3ff1 100644 --- a/README.md +++ b/README.md @@ -113,9 +113,9 @@ You can find more (and newer) screenshots on [the website](https://activitywatch ## Installation & Usage -Downloads are available on our [releases page](https://github.com/ActivityWatch/activitywatch/releases). +Downloads are available on the [releases page](https://github.com/ActivityWatch/activitywatch/releases). -For instructions on how to get started, please see [our guide in the documentation](https://docs.activitywatch.net/en/latest/getting-started.html). +For instructions on how to get started, please see the [guide in the documentation](https://docs.activitywatch.net/en/latest/getting-started.html). Interested in building from source? [There's a guide for that too](https://docs.activitywatch.net/en/latest/installing-from-source.html). @@ -207,7 +207,7 @@ ActivityWatch comes pre-installed with two watchers: - `aw-watcher-afk` tracks the user active/inactive state from keyboard and mouse input - `aw-watcher-window` tracks the currently active application and its window title. -There are lots of other watchers for ActivityWatch which can track more types of activity. Like `aw-watcher-web` which tracks time spent on websites, multiple editor watchers which track spent time coding, and many more! [A full list of watchers can be found in our documentation here](https://docs.activitywatch.net/en/latest/watchers.html). +There are lots of other watchers for ActivityWatch which can track more types of activity. Like `aw-watcher-web` which tracks time spent on websites, multiple editor watchers which track spent time coding, and many more! A full list of watchers can be found in [the documentation](https://docs.activitywatch.net/en/latest/watchers.html). ### Libraries diff --git a/aw-client b/aw-client index a11762c5..5c49cb2c 160000 --- a/aw-client +++ b/aw-client @@ -1 +1 @@ -Subproject commit a11762c523668408d91ffe79ff2944e7003abfa7 +Subproject commit 5c49cb2c6ed31d7c903b46f8c18ec2b317df24fb diff --git a/aw-core b/aw-core index 6f0affa8..3b83ab54 160000 --- a/aw-core +++ b/aw-core @@ -1 +1 @@ -Subproject commit 6f0affa88e9081000333ecfebf85e87f3ee07203 +Subproject commit 3b83ab542406349b3d5e601318289b77d23d906f diff --git a/aw-notify b/aw-notify new file mode 160000 index 00000000..80753ef0 --- /dev/null +++ b/aw-notify @@ -0,0 +1 @@ +Subproject commit 80753ef014fb04ebd97034af8bcefa8be724b6f5 diff --git a/aw-qt b/aw-qt index c47773ac..d20a9419 160000 --- a/aw-qt +++ b/aw-qt @@ -1 +1 @@ -Subproject commit c47773acc587d4b3830604e02a722fbcdb299cf7 +Subproject commit d20a9419c5d94479a633c611c68138886a3ff135 diff --git a/aw-server b/aw-server index 1d5394cb..700f2a84 160000 --- a/aw-server +++ b/aw-server @@ -1 +1 @@ -Subproject commit 1d5394cbf3ac6ebdc99921daec5c8eb07e62a48f +Subproject commit 700f2a847be4cf42fa97a63e9713740ed00b45db diff --git a/aw-server-rust b/aw-server-rust index 78fadbeb..051bb64e 160000 --- a/aw-server-rust +++ b/aw-server-rust @@ -1 +1 @@ -Subproject commit 78fadbebb6f75bfe9b0d4cdc02dc6e441cad6853 +Subproject commit 051bb64ecd310261d3049761c2976a0b8729321f diff --git a/aw-watcher-afk b/aw-watcher-afk index 2d3dd2b0..d30bb84d 160000 --- a/aw-watcher-afk +++ b/aw-watcher-afk @@ -1 +1 @@ -Subproject commit 2d3dd2b045daf2b1b39093406cf9c2f658f65977 +Subproject commit d30bb84d6cb7d36e038ded753cdafecca9a31576 diff --git a/aw-watcher-input b/aw-watcher-input new file mode 160000 index 00000000..b6db2763 --- /dev/null +++ b/aw-watcher-input @@ -0,0 +1 @@ +Subproject commit b6db2763cfbee92c6669ac18e8c4a767392d0810 diff --git a/aw-watcher-window b/aw-watcher-window index f08afe80..12c7bea7 160000 --- a/aw-watcher-window +++ b/aw-watcher-window @@ -1 +1 @@ -Subproject commit f08afe80cc3af1c62f948abb015be0ad3adcec67 +Subproject commit 12c7bea7c866e4eb0575cc8b59ffc9c7b38a645e diff --git a/aw.spec b/aw.spec index 58875560..c3b1e3e6 100644 --- a/aw.spec +++ b/aw.spec @@ -3,14 +3,67 @@ import os import platform -import subprocess import shlex +import subprocess from pathlib import Path +import aw_core import flask_restx -import aw_core +def build_analysis(name, location, binaries=[], datas=[], hiddenimports=[]): + name_py = name.replace("-", "_") + location_candidates = [ + location / f"{name_py}/__main__.py", + location / f"src/{name_py}/__main__.py", + ] + try: + location = next(p for p in location_candidates if p.exists()) + except StopIteration: + raise Exception(f"Could not find {name} location from {location_candidates}") + + return Analysis( + [location], + pathex=[], + binaries=binaries, + datas=datas, + hiddenimports=hiddenimports, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + ) + + +def build_collect(analysis, name, console=True): + """Used to build the COLLECT statements for each module""" + pyz = PYZ(analysis.pure, analysis.zipped_data) + exe = EXE( + pyz, + analysis.scripts, + exclude_binaries=True, + name=name, + debug=False, + strip=False, + upx=True, + console=console, + contents_directory=".", + entitlements_file=entitlements_file, + codesign_identity=codesign_identity, + ) + return COLLECT( + exe, + analysis.binaries, + analysis.zipfiles, + analysis.datas, + strip=False, + upx=True, + name=name, + ) + + +# Get the current release version current_release = subprocess.run( shlex.split("git describe --tags --abbrev=0"), stdout=subprocess.PIPE, @@ -19,6 +72,7 @@ current_release = subprocess.run( ).stdout.strip() print("bundling activitywatch version " + current_release) +# Get entitlements and codesign identity entitlements_file = Path(".") / "scripts" / "package" / "entitlements.plist" codesign_identity = os.environ.get("APPLE_PERSONALID", "").strip() if not codesign_identity: @@ -30,74 +84,46 @@ restx_path = Path(os.path.dirname(flask_restx.__file__)) aws_location = Path("aw-server") aw_server_rust_location = Path("aw-server-rust") aw_server_rust_bin = aw_server_rust_location / "target/package/aw-server-rust" -aw_server_rust_webui = aw_server_rust_location / "target/package/static" +aw_sync_bin = aw_server_rust_location / "target/package/aw-sync" aw_qt_location = Path("aw-qt") awa_location = Path("aw-watcher-afk") aww_location = Path("aw-watcher-window") +awi_location = Path("aw-watcher-input") +aw_notify_location = Path("aw-notify") if platform.system() == "Darwin": icon = aw_qt_location / "media/logo/logo.icns" else: icon = aw_qt_location / "media/logo/logo.ico" -block_cipher = None - -extra_pathex = [] -if platform.system() == "Windows": - # The Windows version includes paths to Qt binaries which are - # not automatically found due to bug in PyInstaller 3.2. - # See: https://github.com/pyinstaller/pyinstaller/issues/2152 - import PyQt5 - - pyqt_path = os.path.dirname(PyQt5.__file__) - extra_pathex.append(pyqt_path + "\\Qt\\bin") skip_rust = False if not aw_server_rust_bin.exists(): skip_rust = True print("Skipping Rust build because aw-server-rust binary not found.") -aw_server_a = Analysis( - ["aw-server/__main__.py"], - pathex=[], - binaries=None, + +aw_qt_a = build_analysis( + "aw-qt", + aw_qt_location, + binaries=[(aw_server_rust_bin, "."), (aw_sync_bin, ".")] if not skip_rust else [], + datas=[ + (aw_qt_location / "resources/aw-qt.desktop", "aw_qt/resources"), + (aw_qt_location / "media", "aw_qt/media"), + ], +) +aw_server_a = build_analysis( + "aw-server", + aws_location, datas=[ (aws_location / "aw_server/static", "aw_server/static"), (restx_path / "templates", "flask_restx/templates"), (restx_path / "static", "flask_restx/static"), (aw_core_path / "schemas", "aw_core/schemas"), ], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, -) - -aw_qt_a = Analysis( - [aw_qt_location / "aw_qt/__main__.py"], - pathex=[] + extra_pathex, - binaries=[(aw_server_rust_bin, ".")] if not skip_rust else [], - datas=[ - (aw_qt_location / "resources/aw-qt.desktop", "aw_qt/resources"), - (aw_qt_location / "media", "aw_qt/media"), - ] - + ([(aw_server_rust_webui, "aw_server_rust/static")] if not skip_rust else []), - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, ) - -aw_watcher_afk_a = Analysis( - [awa_location / "aw_watcher_afk/__main__.py"], - pathex=[], - binaries=None, - datas=None, +aw_watcher_afk_a = build_analysis( + "aw_watcher_afk", + awa_location, hiddenimports=[ "Xlib.keysymdef.miscellany", "Xlib.keysymdef.latin1", @@ -120,35 +146,27 @@ aw_watcher_afk_a = Analysis( "pynput.keyboard._darwin", "pynput.mouse._darwin", ], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, ) - -aw_watcher_window_a = Analysis( - [aww_location / "aw_watcher_window/__main__.py"], - pathex=[], - binaries=[ - ( - aww_location / "aw_watcher_window/aw-watcher-window-macos", - "aw_watcher_window", - ) - ] - if platform.system() == "Darwin" - else [], +aw_watcher_input_a = build_analysis("aw_watcher_input", awi_location) +aw_watcher_window_a = build_analysis( + "aw_watcher_window", + aww_location, + binaries=( + [ + ( + aww_location / "aw_watcher_window/aw-watcher-window-macos", + "aw_watcher_window", + ) + ] + if platform.system() == "Darwin" + else [] + ), datas=[ (aww_location / "aw_watcher_window/printAppStatus.jxa", "aw_watcher_window") ], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, +) +aw_notify_a = build_analysis( + "aw_notify", aw_notify_location, hiddenimports=["desktop_notifier.resources"] ) # https://pythonhosted.org/PyInstaller/spec-files.html#multipackage-bundles @@ -159,110 +177,40 @@ MERGE( (aw_qt_a, "aw-qt", "aw-qt"), (aw_watcher_afk_a, "aw-watcher-afk", "aw-watcher-afk"), (aw_watcher_window_a, "aw-watcher-window", "aw-watcher-window"), + (aw_watcher_input_a, "aw-watcher-input", "aw-watcher-input"), + (aw_notify_a, "aw-notify", "aw-notify"), ) -aww_pyz = PYZ( - aw_watcher_window_a.pure, aw_watcher_window_a.zipped_data, cipher=block_cipher -) -aww_exe = EXE( - aww_pyz, - aw_watcher_window_a.scripts, - exclude_binaries=True, - name="aw-watcher-window", - debug=False, - strip=False, - upx=True, - console=True, - entitlements_file=entitlements_file, - codesign_identity=codesign_identity, -) -aww_coll = COLLECT( - aww_exe, - aw_watcher_window_a.binaries, - aw_watcher_window_a.zipfiles, - aw_watcher_window_a.datas, - strip=False, - upx=True, - name="aw-watcher-window", -) -awa_pyz = PYZ(aw_watcher_afk_a.pure, aw_watcher_afk_a.zipped_data, cipher=block_cipher) -awa_exe = EXE( - awa_pyz, - aw_watcher_afk_a.scripts, - exclude_binaries=True, - name="aw-watcher-afk", - debug=False, - strip=False, - upx=True, - console=True, - entitlements_file=entitlements_file, - codesign_identity=codesign_identity, -) -awa_coll = COLLECT( - awa_exe, - aw_watcher_afk_a.binaries, - aw_watcher_afk_a.zipfiles, - aw_watcher_afk_a.datas, - strip=False, - upx=True, - name="aw-watcher-afk", -) +# aw-server +aws_coll = build_collect(aw_server_a, "aw-server") -aws_pyz = PYZ(aw_server_a.pure, aw_server_a.zipped_data, cipher=block_cipher) +# aw-watcher-window +aww_coll = build_collect(aw_watcher_window_a, "aw-watcher-window") -aws_exe = EXE( - aws_pyz, - aw_server_a.scripts, - exclude_binaries=True, - name="aw-server", - debug=False, - strip=False, - upx=True, - console=True, - entitlements_file=entitlements_file, - codesign_identity=codesign_identity, -) -aws_coll = COLLECT( - aws_exe, - aw_server_a.binaries, - aw_server_a.zipfiles, - aw_server_a.datas, - strip=False, - upx=True, - name="aw-server", -) +# aw-watcher-afk +awa_coll = build_collect(aw_watcher_afk_a, "aw-watcher-afk") -awq_pyz = PYZ(aw_qt_a.pure, aw_qt_a.zipped_data, cipher=block_cipher) -awq_exe = EXE( - awq_pyz, - aw_qt_a.scripts, - exclude_binaries=True, - name="aw-qt", - debug=True, - strip=False, - upx=True, - icon=icon, +# aw-qt +awq_coll = build_collect( + aw_qt_a, + "aw-qt", console=False if platform.system() == "Windows" else True, - entitlements_file=entitlements_file, - codesign_identity=codesign_identity, -) -awq_coll = COLLECT( - awq_exe, - aw_qt_a.binaries, - aw_qt_a.zipfiles, - aw_qt_a.datas, - strip=False, - upx=True, - name="aw-qt", ) +# aw-watcher-input +awi_coll = build_collect(aw_watcher_input_a, "aw-watcher-input") + +aw_notify_coll = build_collect(aw_notify_a, "aw-notify") + if platform.system() == "Darwin": app = BUNDLE( awq_coll, + aws_coll, aww_coll, awa_coll, - aws_coll, + awi_coll, + aw_notify_coll, name="ActivityWatch.app", icon=icon, bundle_identifier="net.activitywatch.ActivityWatch", diff --git a/poetry.lock b/poetry.lock index 7d8d0f66..674ac654 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,41 +1,20 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "altgraph" -version = "0.17.3" +version = "0.17.4" description = "Python graph (network) package" -category = "dev" optional = false python-versions = "*" files = [ - {file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, - {file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, + {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, + {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, ] -[[package]] -name = "attrs" -version = "22.2.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] -tests = ["attrs[tests-no-zope]", "zope.interface"] -tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -45,63 +24,63 @@ files = [ [[package]] name = "coverage" -version = "7.0.5" +version = "7.5.1" description = "Code coverage measurement for Python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"}, - {file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"}, - {file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"}, - {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"}, - {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"}, - {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"}, - {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"}, - {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"}, - {file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"}, - {file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"}, - {file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"}, - {file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"}, - {file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"}, - {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"}, - {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"}, - {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"}, - {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"}, - {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"}, - {file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"}, - {file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"}, - {file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"}, - {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"}, - {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"}, - {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"}, - {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"}, - {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"}, - {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"}, - {file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"}, - {file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"}, - {file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"}, - {file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"}, - {file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"}, - {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"}, - {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"}, - {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"}, - {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"}, - {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"}, - {file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"}, - {file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"}, - {file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"}, - {file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"}, - {file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"}, - {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"}, - {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"}, - {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"}, - {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"}, - {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"}, - {file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"}, - {file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"}, - {file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"}, - {file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, + {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, + {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, + {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, + {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, + {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, + {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, ] [package.dependencies] @@ -112,35 +91,41 @@ toml = ["tomli"] [[package]] name = "exceptiongroup" -version = "1.1.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, - {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] -name = "future" -version = "0.18.2" -description = "Clean single-source support for Python 3 and 2" -category = "dev" +name = "importlib-metadata" +version = "7.1.0" +description = "Read metadata from Python packages" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.8" files = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -150,14 +135,13 @@ files = [ [[package]] name = "macholib" -version = "1.16.2" +version = "1.16.3" description = "Mach-O header analysis and editing" -category = "dev" optional = false python-versions = "*" files = [ - {file = "macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"}, - {file = "macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"}, + {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, + {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, ] [package.dependencies] @@ -165,103 +149,93 @@ altgraph = ">=0.17" [[package]] name = "mypy" -version = "0.991" +version = "1.10.0" description = "Optional static typing for Python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, - {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, - {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, - {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, - {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, - {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, - {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, - {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, - {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, - {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, - {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, - {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, - {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, - {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, - {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, - {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, - {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, - {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, - {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, - {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, - {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, - {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, - {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, - {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, ] [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "packaging" -version = "23.0" +version = "24.0" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pefile" -version = "2022.5.30" +version = "2023.2.7" description = "Python PE parsing module" -category = "dev" optional = false python-versions = ">=3.6.0" files = [ - {file = "pefile-2022.5.30.tar.gz", hash = "sha256:a5488a3dd1fd021ce33f969780b88fe0f7eebb76eb20996d7318f307612a045b"}, + {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, + {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, ] -[package.dependencies] -future = "*" - [[package]] name = "pluggy" -version = "1.0.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -270,26 +244,27 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "psutil" -version = "5.9.4" +version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, - {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, - {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, - {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, - {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, - {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, - {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, - {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, ] [package.extras] @@ -299,7 +274,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" -category = "dev" optional = false python-versions = "*" files = [ @@ -309,64 +283,71 @@ files = [ [[package]] name = "pyinstaller" -version = "5.7.0" +version = "6.6.0" description = "PyInstaller bundles a Python application and all its dependencies into a single package." -category = "dev" optional = false -python-versions = "<3.12,>=3.7" +python-versions = "<3.13,>=3.8" files = [ - {file = "pyinstaller-5.7.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:b967ae71ab7b05e18608dbb4518da5afa54f0835927cb7a5ce52ab8fffed03b6"}, - {file = "pyinstaller-5.7.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3180b9bf22263380adc5e2ee051b7c21463292877215bbe70c9155dc76f4b966"}, - {file = "pyinstaller-5.7.0-py3-none-manylinux2014_i686.whl", hash = "sha256:0f80e2403e76630ad3392c71f09c1a4284e8d8a8a99fb55ff3a0aba0e06300ed"}, - {file = "pyinstaller-5.7.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:2c1dd9d11cfc48bab61eeb06de69a3d1ad742bbb2ef14716965ca0333dd43a5b"}, - {file = "pyinstaller-5.7.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:dfc12e92fe10ae645dd0dd1fcfa4cd7677b2e96119e3cd4980d742e09bb78925"}, - {file = "pyinstaller-5.7.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f35f06d48faea0ad738429c009941059beebaa306e9d9ead95f1df4b441de2aa"}, - {file = "pyinstaller-5.7.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:28a8a0da656493aa32d9665e2f6f84775da0f23174859ed8facaa4226fe77a17"}, - {file = "pyinstaller-5.7.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:1ac3f09b838710c43e34b0a7ad003bd168a754b0b786c561b47baf1af9104354"}, - {file = "pyinstaller-5.7.0-py3-none-win32.whl", hash = "sha256:9cdb8ee8622ee8d2c6cd67f001b610019d4371a8bf3f7850562640ce786894d7"}, - {file = "pyinstaller-5.7.0-py3-none-win_amd64.whl", hash = "sha256:9b47c10fbefac6f6493266f8b1689109b2b14efa9142dbd2cd7549226a4568b7"}, - {file = "pyinstaller-5.7.0-py3-none-win_arm64.whl", hash = "sha256:3e51e18a16dec0414079762843cf892a5d70749ad56ca7b3c7b5f8367dc50b1e"}, - {file = "pyinstaller-5.7.0.tar.gz", hash = "sha256:0e5953937d35f0b37543cc6915dacaf3239bcbdf3fd3ecbb7866645468a16775"}, + {file = "pyinstaller-6.6.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:d2705efe79f8749526f65c4bce70ae88eea8b6adfb051f123122e86542fe3802"}, + {file = "pyinstaller-6.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:2aa771693ee3e0a899be3e9d946a24eab9896a98d0d4035f05a22f1193004cfb"}, + {file = "pyinstaller-6.6.0-py3-none-manylinux2014_i686.whl", hash = "sha256:1fc15e8cebf76361568359a40926aa5746fc0a84ca365fb2ac6caeea014a2cd3"}, + {file = "pyinstaller-6.6.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:7c4a55a5d872c118bc7a5e641c2df46ad18585c002d96adad129b4ee8c104463"}, + {file = "pyinstaller-6.6.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:97197593344f11f3dd2bdadbab14c61fbc4cdf9cc692a89b047cb671764c1824"}, + {file = "pyinstaller-6.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:00d81ddeee97710245a7ed03b0f9d5a4daf6c3a07adf978487b10991e1e20470"}, + {file = "pyinstaller-6.6.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:b7cab21db6fcfbdab47ee960239d1b44cd95383a4463177bd592613941d67959"}, + {file = "pyinstaller-6.6.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:00996d2090734d9ae4a1e53ed40351b07d593c37118d3e0d435bbcfa8db9edee"}, + {file = "pyinstaller-6.6.0-py3-none-win32.whl", hash = "sha256:cfe3ed214601de0723cb660994b44934efacb77a1cf0e4cc5133da996bcf36ce"}, + {file = "pyinstaller-6.6.0-py3-none-win_amd64.whl", hash = "sha256:e2f55fbbdf8a99ea84b39bc5669a68624473c303486d7eb2cd9063b339f0aa28"}, + {file = "pyinstaller-6.6.0-py3-none-win_arm64.whl", hash = "sha256:abbd591967593dab264bcc3bcb2466c0a1582f19a112e37e916c4212069c7933"}, + {file = "pyinstaller-6.6.0.tar.gz", hash = "sha256:be6bc2c3073d3e84fb7148d3af33ce9b6a7f01cfb154e06314cd1d4c05798a32"}, ] [package.dependencies] altgraph = "*" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +packaging = ">=22.0" pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} -pyinstaller-hooks-contrib = ">=2021.4" -pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2024.3" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} setuptools = ">=42.0.0" [package.extras] -encryption = ["tinyaes (>=1.0.0)"] +completion = ["argcomplete"] hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2022.14" +version = "2024.6" description = "Community maintained hooks for PyInstaller" -category = "dev" optional = false python-versions = ">=3.7" -files = [ - {file = "pyinstaller-hooks-contrib-2022.14.tar.gz", hash = "sha256:5ae8da3a92cf20e37b3e00604d0c3468896e7d746e5c1449473597a724331b0b"}, - {file = "pyinstaller_hooks_contrib-2022.14-py2.py3-none-any.whl", hash = "sha256:1a125838a22d7b35a18993c6e56d3c5cc3ad7da00954f95bc5606523939203f2"}, -] +files = [] +develop = false + +[package.dependencies] +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +packaging = ">=22.0" +setuptools = ">=42.0.0" + +[package.source] +type = "git" +url = "https://github.com/pyinstaller/pyinstaller-hooks-contrib.git" +reference = "master" +resolved_reference = "3ce4ae66ccf5996aef779be9223d03471611b868" [[package]] name = "pytest" -version = "7.2.0" +version = "7.4.4" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, - {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" @@ -375,13 +356,12 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -400,14 +380,13 @@ histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" -version = "4.0.0" +version = "5.0.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, - {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] @@ -415,42 +394,39 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pywin32-ctypes" -version = "0.2.0" -description = "" -category = "dev" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ - {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, - {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, ] [[package]] name = "setuptools" -version = "65.6.3" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, - {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -460,17 +436,47 @@ files = [ [[package]] name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] +[[package]] +name = "urllib3" +version = "1.26.18" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, +] + +[package.extras] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" -python-versions = "^3.8,<3.11" -content-hash = "1420c29960f3ef20068dd73a016bac91e3849b882b1f4bf6b3890c904030c4f4" +python-versions = "^3.8,<3.13" +content-hash = "d7af47afd4056faeaf0fb5e47010d15c208e29d0bbba6637beec23bdc90bcfee" diff --git a/pyproject.toml b/pyproject.toml index aaff575c..2a884933 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Erik Bjäreholt ", "Johan Bjäreholt str: """Generates links from commit and issue references (like 0c14d77, #123) to correct repo and such""" s = self.msg s = re.sub( - r"[^(-]https://github.com/ActivityWatch/([\-\w\d]+)/(issues|pulls)/(\d+)", - r"[#\3](https://github.com/ActivityWatch/\1/issues/\3)", + rf"[^(-]https://github.com/{self.org}/([\-\w\d]+)/(issues|pulls)/(\d+)", + rf"[#\3](https://github.com/{self.org}/\1/issues/\3)", s, ) s = re.sub( r"#(\d+)", - rf"[#\1](https://github.com/ActivityWatch/{self.repo}/issues/\1)", + rf"[#\1](https://github.com/{self.org}/{self.repo}/issues/\1)", s, ) s = re.sub( r"[\s\(][0-9a-f]{7}[\s\)]", - rf"[`\0`](https://github.com/ActivityWatch/{self.repo}/issues/\0)", + rf"[`\0`](https://github.com/{self.org}/{self.repo}/issues/\0)", s, ) return s @@ -96,7 +149,7 @@ def type_str(self) -> str: return f"{_type}" + (f"({subtype})" if subtype else "") def format(self) -> str: - commit_link = commit_linkify(self.id, self.repo) if self.id else "" + commit_link = commit_linkify(self.id, self.org, self.repo) if self.id else "" return f"{self.msg_processed}" + (f" ({commit_link})" if commit_link else "") @@ -111,59 +164,64 @@ def run(cmd, cwd=".") -> str: return p.stdout -def pr_linkify(prid: str, repo: str) -> str: - return f"[#{prid}](https://github.com/ActivityWatch/{repo}/pulls/{prid})" +def pr_linkify(prid: str, org: str, repo: str) -> str: + return f"[#{prid}](https://github.com/{org}/{repo}/pulls/{prid})" -def commit_linkify(commitid: str, repo: str) -> str: - return f"[`{commitid}`](https://github.com/ActivityWatch/{repo}/commit/{commitid})" +def commit_linkify(commitid: str, org: str, repo: str) -> str: + return f"[`{commitid}`](https://github.com/{org}/{repo}/commit/{commitid})" def wrap_details(title, body, wraplines=5): """Wrap lines into a
element if body is longer than `wraplines`""" out = f"\n\n### {title}" - if body.count("\n") > wraplines: - out += "\n
Click to expand" - out += f"\n

\n\n{body.rstrip()}\n\n

\n" - if body.count("\n") > wraplines: - out += "
" + wrap = body.strip().count("\n") > wraplines + if wrap: + out += "\n
Click to expand\n

" + out += f"\n{body.rstrip()}" + if wrap: + out += "\n\n

\n
" return out contributor_emails = set() -def summary_repo(path: str, commitrange: str, filter_types: List[str]) -> str: - if commitrange.endswith("0000000"): +def summary_repo( + org: str, + repo: str, + path: str, + commit_range: Tuple[str, str], + filter_types: List[str], + repo_order: List[str], +) -> str: + if commit_range[1] == "0000000": # Happens when a submodule has been removed return "" - if commitrange.startswith("0000000"): + if commit_range[0] == "0000000": # Happens when a submodule has been added - commitrange = "" # no range = all commits + commit_range = ("", "") # no range = all commits for new submodule - dirname = run("bash -c 'basename $(pwd)'", cwd=path).strip() - out = f"\n## 📦 {dirname}" + out = f"\n## 📦 {repo}" feats = "" fixes = "" misc = "" + hidden = 0 # pretty format is modified version of: https://stackoverflow.com/a/1441062/965332 summary_bundle = run( - f"git log {commitrange} --no-decorate --pretty=format:'%h%x09%an%x09%ae%x09%s'", + f"git log {'...'.join(commit_range) if any(commit_range) else ''} --no-decorate --pretty=format:'%h%x09%an%x09%ae%x09%s'", cwd=path, ) + print(f"Found {len(summary_bundle.splitlines())} commits in {repo}") for line in summary_bundle.split("\n"): if line: _id, _author, email, msg = line.split("\t") # will add author email to contributor list # the `contributor_emails` is global and collected later contributor_emails.add(email) - commit = Commit( - id=_id, - msg=msg, - repo=dirname, - ) + commit = Commit(id=_id, msg=msg, org=org, repo=repo) entry = f"\n - {commit.format()}" if commit.type == "feat": @@ -172,6 +230,8 @@ def summary_repo(path: str, commitrange: str, filter_types: List[str]) -> str: fixes += entry elif commit.type not in filter_types: misc += entry + else: + hidden += 1 for name, entries in (("✨ Features", feats), ("🐛 Fixes", fixes), ("🔨 Misc", misc)): if entries: @@ -180,14 +240,17 @@ def summary_repo(path: str, commitrange: str, filter_types: List[str]) -> str: if "Misc" in name or "Fixes" in name: out += wrap_details(title, entries) else: - out += f"\n\n### {title}" + out += f"\n\n### {title}\n" out += entries + if hidden > 1: + full_history_url = f"https://github.com/{org}/{repo}/compare/{commit_range[0]}...{commit_range[1]}" + out += f"\n\n*(excluded {hidden} less relevant [commits]({full_history_url}))*" # NOTE: For now, these TODOs can be manually fixed for each changelog. # TODO: Fix issue where subsubmodules can appear twice (like aw-webui) # TODO: Use specific order (aw-webui should be one of the first, for example) summary_subrepos = run( - f"git submodule summary {commitrange.split('...')[0]}", cwd=path + f"git submodule summary --cached {commit_range[0]}", cwd=path ) subrepos = {} for header, *_ in [s.split("\n") for s in summary_subrepos.split("\n\n")]: @@ -199,17 +262,30 @@ def summary_repo(path: str, commitrange: str, filter_types: List[str]) -> str: # Submodule may have been deleted continue - _, name, commitrange, count = header.split(" ") + _, name, crange, count = header.split(" ") + commit_range = tuple(crange.split("...", 1)) # type: ignore count = count.strip().lstrip("(").rstrip("):") logger.info( - f"Found {name}, looking up range: {commitrange} ({count} commits)" + f"Found {name}, looking up range: {commit_range} ({count} commits)" ) name = name.strip(".").strip("/") subrepos[name] = summary_repo( - f"{path}/{name}", commitrange, filter_types=filter_types + org, + name, + f"{path}/{name}", + commit_range, + filter_types=filter_types, + repo_order=repo_order, ) + # filter out subrepos with no commits (single line after stripping whitespace) + subrepos = { + name: output + for name, output in subrepos.items() + if len(output.strip().splitlines()) > 1 + } + # pick subrepos in repo_order, and remove from dict for name in repo_order: if name in subrepos: @@ -261,43 +337,45 @@ def remove_duplicates(s: List[str], minlen=10, only_sections=True) -> List[str]: return out -def build(filter_types=["build", "ci", "tests", "test"]): - prev_release = run("git describe --tags --abbrev=0").strip() - next_release = "master" - - parser = argparse.ArgumentParser(description="Generate changelog from git history") - parser.add_argument( - "--range", default=f"{prev_release}...{next_release}", help="Git commit range" - ) - parser.add_argument("--path", default=".", help="Path to git repo") - parser.add_argument( - "--output", default="changelog.md", help="Path to output changelog" - ) - args = parser.parse_args() - - since, until = args.range.split("...") - tag = until - +def build( + org: str, + repo: str, + project_name: str, + commit_range: Tuple[str, str], + output_path: str, + repo_order: List[str], + filter_types=["build", "ci", "tests", "test"], +): # provides a commit summary for the repo and subrepos, recursively looking up subrepos # NOTE: this must be done *before* `get_all_contributors` is called, # as the latter relies on summary_repo looking up all users and storing in a global. logger.info("Generating commit summary") + since, tag = commit_range output_changelog = summary_repo( - ".", commitrange=args.range, filter_types=filter_types + org, + repo, + ".", + commit_range=commit_range, + filter_types=filter_types, + repo_order=repo_order, ) output_changelog = f""" # Changelog -Changes since {since} +Changes since {since}: {output_changelog} """.strip() # Would ideally sort by number of commits or something, but that's tricky usernames = sorted(get_all_contributors(), key=str.casefold) + usernames = [u for u in usernames if not u.endswith("[bot]")] twitter_handles = get_twitter_of_ghusers(usernames) - print(", ".join("@" + handle for handle in twitter_handles.values() if handle)) + print( + "Twitter handles: " + + ", ".join("@" + handle for handle in twitter_handles.values() if handle), + ) output_contributors = f"""# Contributors @@ -307,31 +385,37 @@ def build(filter_types=["build", "ci", "tests", "test"]): # Header starts here logger.info("Building final output") - output = f"""# {tag}""" - output += "\n\n" - output += f"These are the release notes for ActivityWatch version {tag}.".strip() + output = f"These are the release notes for {project_name} version {tag}.".strip() output += "\n\n" - output += "**New to ActivityWatch?** Check out the [website](https://activitywatch.net) and the [README](https://github.com/ActivityWatch/activitywatch/blob/master/README.md)." - output += "\n\n" - output += """# Installation + + # hardcoded for now + if repo == "activitywatch": + output += "**New to ActivityWatch?** Check out the [website](https://activitywatch.net) and the [README](https://github.com/ActivityWatch/activitywatch/blob/master/README.md)." + output += "\n\n" + output += """# Installation See the [getting started guide in the documentation](https://docs.activitywatch.net/en/latest/getting-started.html). - """.strip() - output += "\n\n" - output += f"""# Downloads + """.strip() + output += "\n\n" + output += f"""# Downloads - [**Windows**](https://github.com/ActivityWatch/activitywatch/releases/download/{tag}/activitywatch-{tag}-windows-x86_64-setup.exe) (.exe, installer) - [**macOS**](https://github.com/ActivityWatch/activitywatch/releases/download/{tag}/activitywatch-{tag}-macos-x86_64.dmg) (.dmg) - [**Linux**](https://github.com/ActivityWatch/activitywatch/releases/download/{tag}/activitywatch-{tag}-linux-x86_64.zip) (.zip) - """.strip() - output += "\n\n" + """.strip() + output += "\n\n" + output += output_contributors.strip() + "\n\n" output += output_changelog.strip() + "\n\n" + output += ( + f"**Full Changelog**: https://github.com/{org}/{repo}/compare/{since}...{tag}" + ) - output = output.replace("# activitywatch", "# activitywatch (bundle repo)") - with open(args.output, "w") as f: + if repo == "activitywatch": + output = output.replace("# activitywatch", "# activitywatch (bundle repo)") + with open(output_path, "w") as f: f.write(output) - print(f"Wrote {len(output.splitlines())} lines to {args.output}") + print(f"Wrote {len(output.splitlines())} lines to {output_path}") def _resolve_email(email: str) -> Optional[str]: @@ -390,7 +474,7 @@ def get_all_contributors() -> set[str]: logger.info("Getting all contributors") # We will commit this file, to act as a cache (preventing us from querying GitHub API every time) - filename = "scripts/changelog_contributors.csv" + filename = script_dir / "changelog_contributors.csv" # mapping from username to one or more emails usernames: Dict[str, set] = defaultdict(set) @@ -401,6 +485,8 @@ def get_all_contributors() -> set[str]: usernames["kewde"] |= {"kewde@particl.io"} usernames["victorwinberg"] |= {"victor.m.winberg@gmail.com"} usernames["NicoWeio"] |= {"nico.weio@gmail.com"} + usernames["2e3s"] |= {"2e3s19@gmail.com"} + usernames["alwinator"] |= {"accounts@alwinschuster.at"} # read existing contributors, to avoid extra calls to the GitHub API if os.path.exists(filename): @@ -444,8 +530,26 @@ def get_all_contributors() -> set[str]: def get_twitter_of_ghusers(ghusers: Collection[str]): logger.info("Getting twitter of GitHub usernames") + + # We will commit this file, to act as a cache (preventing us from querying GitHub API every time) + filename = script_dir / "changelog_contributors_twitter.csv" + twitter = {} + + # read existing contributors, to avoid extra calls to the GitHub API + if os.path.exists(filename): + with open(filename, "r") as f: + s = f.read() + for line in s.split("\n"): + if not line: + continue + gh_username, twitter_username = line.split("\t") + twitter[gh_username] = twitter_username + logger.info(f"Read {len(twitter)} Twitter handles from {filename}") + for username in ghusers: + if username in twitter: + continue try: resp = requests.get(f"https://api.github.com/users/{username}") resp.raise_for_status() @@ -454,9 +558,17 @@ def get_twitter_of_ghusers(ghusers: Collection[str]): logger.warning(f"Failed to get twitter of {username}: {e}") continue - twitter[username] = data["twitter_username"] + twitter_username = data["twitter_username"] + if twitter_username: + twitter[username] = twitter_username + + with open(filename, "w") as f: + for username, twitter_username in sorted(twitter.items()): + f.write(f"{username}\t{twitter_username}") + f.write("\n") + return twitter if __name__ == "__main__": - build() + main() diff --git a/scripts/changelog_contributors.csv b/scripts/changelog_contributors.csv index 2d4fb0c4..d05f6a64 100644 --- a/scripts/changelog_contributors.csv +++ b/scripts/changelog_contributors.csv @@ -1,23 +1,31 @@ +2e3s 2e3s19@gmail.com +750 37119951+750@users.noreply.github.com Alwinator 39517491+Alwinator@users.noreply.github.com BelKed 66956532+BelKed@users.noreply.github.com CrazyPython Jamtlu@gmail.com Drarig29 corentingirard.dev@gmail.com Furffico 43836984+Furffico@users.noreply.github.com GabLeRoux lebreton.gabriel@gmail.com +Julianoe Julianoe@users.noreply.github.com LockBlock-dev 68129141+LockBlock-dev@users.noreply.github.com LunarWatcher zoe.i2k1@gmail.com NicoWeio kontakt@nicolaiweitkemper.de nico.weio@gmail.com ShootingKing-AM narnindi.raghu@gmail.com Shubham0324 53115519+Shubham0324@users.noreply.github.com +StefanoChiodino StefanoChiodino@users.noreply.github.com +TSRBerry 20988865+TSRBerry@users.noreply.github.com Valentin-N 1926716+Valentin-N@users.noreply.github.com Y7n05h Y7n05h@protonmail.com aaayushsingh ayush-_-singh@live.com alclary 9044153+alclary@users.noreply.github.com alialamine ali@towbe.com +alwinator accounts@alwinschuster.at +chaoky levimanga@gmail.com chengyuhui chengyuhui1@gmail.com dependabot-preview[bot] 27856297+dependabot-preview[bot]@users.noreply.github.com dependabot[bot] 49699333+dependabot[bot]@users.noreply.github.com erikbjare erik.bjareholt@gmail.com erik@bjareho.lt +hooger hooger@users.noreply.github.com iloveitaly iloveitaly@gmail.com infokiller infokiller@users.noreply.github.com ishitatsuyuki ishitatsuyuki@gmail.com @@ -30,15 +38,18 @@ luzpaz luzpaz@users.noreply.github.com maciekstosio maciekstosio@users.noreply.github.com michaeljelly 53475252+michaeljelly@users.noreply.github.com modderme123 modderme123@users.noreply.github.com +ochen1 o.chen1@share.epsb.ca omahs 73983677+omahs@users.noreply.github.com oscar-king oscar-king@users.noreply.github.com pktiuk kotiuk@zohomail.eu rakleed 19418601+rakleed@users.noreply.github.com repo-visualizer repo-visualizer@users.noreply.github.com salahineo salahineo.personal@gmail.com +skewballfox joshua.ferguson.273@gmail.com soxofaan soxofaan@users.noreply.github.com sunrosa 79175772+sunrosa@users.noreply.github.com vedantmgoyal2009 83997633+vedantmgoyal2009@users.noreply.github.com +victorlin 13424970+victorlin@users.noreply.github.com victorwinberg victor.m.winberg@gmail.com xylix kerk.pelt@gmail.com yumemio 59369226+yumemio@users.noreply.github.com diff --git a/scripts/changelog_contributors_twitter.csv b/scripts/changelog_contributors_twitter.csv new file mode 100644 index 00000000..aee97991 --- /dev/null +++ b/scripts/changelog_contributors_twitter.csv @@ -0,0 +1,5 @@ +chaoky chaokyer +erikbjare erikbjare +iloveitaly mike_bianco +vedantmgoyal2009 vedantmgoyal +victorlin victorlin_ diff --git a/scripts/get_latest_release.sh b/scripts/get_latest_release.sh index 8c58c93c..219eb8fb 100755 --- a/scripts/get_latest_release.sh +++ b/scripts/get_latest_release.sh @@ -1,18 +1,22 @@ #!/bin/bash +# TODO: Merge with scripts/package/getversion.sh + # Script that fetches the previous release (if current commit is a tag), # or the latest release, if current commit is not a tag. -# If not stable only, then we return either the latest prerelease, or if none, the latest stable release -RE='(?<=[/])v[0-9\.]+(a|b|rc)[0-9]+$' +# If stable only, then we return the latest stable release, +# else, we will return the latest release, either stable or prerelease. +RE_STABLE='(?<=[/])v[0-9\.]+$' +RE_INCL_PRERELEASE='(?<=[/])v[0-9\.]+(a|b|rc)?[0-9]+$' # Get tag for this commit, if any TAG=$(git describe --tags --exact-match 2>/dev/null) +RE=$RE_INCL_PRERELEASE if [ -n "$STABLE_ONLY" ]; then if [ "$STABLE_ONLY" = "true" ]; then - # If stable only, then we only want to return the latest stable version - RE='(?<=[/])v[0-9\.]+$' + RE=$RE_STABLE fi fi ALL_TAGS=`git for-each-ref --sort=creatordate --format '%(refname)' refs/tags` diff --git a/scripts/package/activitywatch-setup.iss b/scripts/package/activitywatch-setup.iss index 306a8d8e..dc582e59 100644 --- a/scripts/package/activitywatch-setup.iss +++ b/scripts/package/activitywatch-setup.iss @@ -14,7 +14,7 @@ [Setup] ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) -; TODO: Should probably remove the extra leading {, but don't want to risk changing the AppId... +; NOTE: the double {{ are used to escape the { character (needed for the AppId) AppId={{F226B8F4-3244-46E6-901D-0CE8035423E4} AppName={#MyAppName} AppVersion={#MyAppVersion} diff --git a/scripts/package/getversion.sh b/scripts/package/getversion.sh index 430f638c..9c237975 100755 --- a/scripts/package/getversion.sh +++ b/scripts/package/getversion.sh @@ -1,5 +1,6 @@ #!/bin/bash +# TODO: Merge with scripts/package/getversion.sh # set -e if [[ $TRAVIS_TAG ]]; then diff --git a/scripts/package/package-appimage.sh b/scripts/package/package-appimage.sh index b002ffca..dd4ece93 100755 --- a/scripts/package/package-appimage.sh +++ b/scripts/package/package-appimage.sh @@ -18,6 +18,6 @@ DIR="$(dirname "$(readlink -f "${0}")")" chmod a+x ./activitywatch/AppRun # build appimage -./linuxdeploy-x86_64.AppImage --appdir activitywatch --executable ./activitywatch/aw-qt --output appimage --desktop-file ./activitywatch/aw-qt.desktop --icon-file ./activitywatch/aw-server-rust/static/static/logo.png --icon-filename activitywatch +./linuxdeploy-x86_64.AppImage --appdir activitywatch --executable ./activitywatch/aw-qt --output appimage --desktop-file ./activitywatch/aw-qt.desktop --icon-file ./activitywatch/media/logo/logo.png --icon-filename activitywatch APPIMAGE_FILE=`ls -1 | grep AppImage| grep -i ActivityWatch` cp -v $APPIMAGE_FILE ./dist/activitywatch-linux-x86_64.AppImage diff --git a/scripts/tests/integration_tests.py b/scripts/tests/integration_tests.py index ce70b83f..335ac67a 100644 --- a/scripts/tests/integration_tests.py +++ b/scripts/tests/integration_tests.py @@ -1,25 +1,39 @@ +import os +import platform import subprocess -from time import sleep import tempfile -import platform +from time import sleep import pytest def _windows_kill_process(pid): import ctypes + PROCESS_TERMINATE = 1 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid) ctypes.windll.kernel32.TerminateProcess(handle, -1) ctypes.windll.kernel32.CloseHandle(handle) +# NOTE: to run tests with a specific server binary, +# set the PATH such that it is the "aw-server" binary. @pytest.fixture(scope="session") def server_process(): logfile_stdout = tempfile.NamedTemporaryFile(delete=False) logfile_stderr = tempfile.NamedTemporaryFile(delete=False) - server_proc = subprocess.Popen(["aw-server", "--testing"], stdout=logfile_stdout, stderr=logfile_stderr) + # find the path of the "aw-server" binary and log it + which_server = subprocess.check_output(["which", "aw-server"], text=True) + print(f"aw-server path: {which_server}") + + # if aw-server-rust in PATH, assert that we're picking up the aw-server-rust binary + if "aw-server-rust" in os.environ["PATH"]: + assert "aw-server-rust" in which_server + + server_proc = subprocess.Popen( + ["aw-server", "--testing"], stdout=logfile_stdout, stderr=logfile_stderr + ) # Wait for server to start up properly # TODO: Ping the server until it's alive to remove this sleep @@ -40,19 +54,20 @@ def server_process(): with open(logfile_stdout.name, "r+b") as f: stdout = str(f.read(), "utf8") if any(e in stdout for e in error_indicators): - pytest.fail("Found ERROR indicator in stdout from server: {}".format(stdout)) + pytest.fail(f"Found ERROR indicator in stdout from server: {stdout}") with open(logfile_stderr.name, "r+b") as f: stderr = str(f.read(), "utf8") - if not stderr: - pytest.fail("No output to stderr from server") + # For some reason, this fails aw-server-rust, but not aw-server-python + # if not stderr: + # pytest.fail("No output to stderr from server") # Will show in case pytest fails print(stderr) for s in error_indicators: if s in stderr: - pytest.fail("Found ERROR indicator in stderr from server: {}".format(s)) + pytest.fail(f"Found ERROR indicator in stderr from server: {s}") # NOTE: returncode was -9 for whatever reason # if server_proc.returncode != 0: