diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ec33d5..ccf5516 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,8 +2,9 @@ # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions # Based on ~/code/xcookie/xcookie/rc/tests.yml.in # Now based on ~/code/xcookie/xcookie/builders/github_actions.py +# See: https://github.com/Erotemic/xcookie -name: PurePy Build and Test +name: PurePyCI on: push: @@ -12,14 +13,19 @@ on: jobs: lint_job: + ## + # Run quick linting and typing checks. + # To disable all linting add "linter=false" to the xcookie config. + # To disable type checks add "notypes" to the xcookie tags. + ## runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v3 - - name: Set up Python 3.8 - uses: actions/setup-python@v4.5.0 + uses: actions/checkout@v4.1.1 + - name: Set up Python 3.12 for linting + uses: actions/setup-python@v5.0.0 with: - python-version: 3.8 + python-version: '3.12' - name: Install dependencies run: |- python -m pip install --upgrade pip @@ -34,21 +40,25 @@ jobs: mypy --install-types --non-interactive ./plottool_ibeis mypy ./plottool_ibeis build_and_test_sdist: - name: Test sdist Python 3.8 + ## + # Build the pure python package from source and test it in the + # same environment. + ## + name: Build sdist runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v3 - - name: Set up Python 3.8 - uses: actions/setup-python@v4.5.0 + uses: actions/checkout@v4.1.1 + - name: Set up Python 3.12 + uses: actions/setup-python@v5.0.0 with: - python-version: 3.8 + python-version: '3.12' - name: Upgrade pip run: |- python -m pip install --upgrade pip - python -m pip install -r requirements/tests.txt - python -m pip install -r requirements/runtime.txt - python -m pip install -r requirements/headless.txt + python -m pip install --prefer-binary -r requirements/tests.txt + python -m pip install --prefer-binary -r requirements/runtime.txt + python -m pip install --prefer-binary -r requirements/headless.txt - name: Build sdist shell: bash run: |- @@ -57,8 +67,8 @@ jobs: python -m build --sdist --outdir wheelhouse - name: Install sdist run: |- - ls -al ./wheelhouse - pip install wheelhouse/plottool_ibeis*.tar.gz -v + ls -al wheelhouse + pip install --prefer-binary wheelhouse/plottool_ibeis*.tar.gz -v - name: Test minimal loose sdist run: |- pwd @@ -71,13 +81,13 @@ jobs: # Get path to installed package MOD_DPATH=$(python -c "import plottool_ibeis, os; print(os.path.dirname(plottool_ibeis.__file__))") echo "MOD_DPATH = $MOD_DPATH" - python -m pytest --cov={self.mod_name} $MOD_DPATH ../tests + python -m pytest --verbose --cov=plottool_ibeis $MOD_DPATH ../tests cd .. - name: Test full loose sdist run: |- pwd ls -al - python -m pip install -r requirements/headless.txt + python -m pip install --prefer-binary -r requirements/headless.txt # Run in a sandboxed directory WORKSPACE_DNAME="testsrcdir_full_${CI_PYTHON_VERSION}_${GITHUB_RUN_ID}_${RUNNER_OS}" mkdir -p $WORKSPACE_DNAME @@ -86,207 +96,274 @@ jobs: # Get path to installed package MOD_DPATH=$(python -c "import plottool_ibeis, os; print(os.path.dirname(plottool_ibeis.__file__))") echo "MOD_DPATH = $MOD_DPATH" - python -m pytest --cov={self.mod_name} $MOD_DPATH ../tests + python -m pytest --verbose --cov=plottool_ibeis $MOD_DPATH ../tests cd .. - - name: Upload sdist artifact - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v3.1.3 + name: Upload sdist artifact + with: + name: sdist_wheels + path: wheelhouse/*.tar.gz + build_purepy_wheels: + ## + # Download and test the pure-python wheels that were build in the + # build_purepy_wheels and test them in this independent environment. + ## + name: ${{ matrix.python-version }} on ${{ matrix.os }}, arch=${{ matrix.arch }} with ${{ matrix.install-extras }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + python-version: + - '3.12' + arch: + - auto + steps: + - name: Checkout source + uses: actions/checkout@v4.1.1 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + if: runner.os == 'Linux' && matrix.arch != 'auto' + with: + platforms: all + - name: Setup Python + uses: actions/setup-python@v5.0.0 + with: + python-version: ${{ matrix.python-version }} + - name: Build pure wheel + shell: bash + run: |- + python -m pip install setuptools>=0.8 wheel build twine + python -m build --wheel --outdir wheelhouse + python -m twine check ./wheelhouse/plottool_ibeis*.whl + - name: Show built files + shell: bash + run: ls -la wheelhouse + - uses: actions/upload-artifact@v3.1.3 + name: Upload wheels artifact with: name: wheels - path: ./wheelhouse/*.tar.gz - build_and_test_purepy_wheels: + path: ./wheelhouse/plottool_ibeis*.whl + test_purepy_wheels: name: ${{ matrix.python-version }} on ${{ matrix.os }}, arch=${{ matrix.arch }} with ${{ matrix.install-extras }} + if: "! startsWith(github.event.ref, 'refs/heads/release')" runs-on: ${{ matrix.os }} + needs: + - build_purepy_wheels strategy: + fail-fast: false matrix: + # Xcookie generates an explicit list of environments that will be used + # for testing instead of using the more concise matrix notation. include: - - python-version: '3.7' - os: ubuntu-latest + - python-version: '3.8' install-extras: tests-strict,runtime-strict,headless-strict + os: ubuntu-latest arch: auto - - python-version: '3.7' - os: macOS-latest + - python-version: '3.8' install-extras: tests-strict,runtime-strict,headless-strict + os: macOS-latest arch: auto - - python-version: '3.7' - os: windows-latest + - python-version: '3.8' install-extras: tests-strict,runtime-strict,headless-strict + os: windows-latest arch: auto - - python-version: '3.10' - os: ubuntu-latest + - python-version: '3.12' install-extras: tests-strict,runtime-strict,optional-strict,headless-strict + os: ubuntu-latest arch: auto - - python-version: '3.10' - os: macOS-latest + - python-version: '3.12' install-extras: tests-strict,runtime-strict,optional-strict,headless-strict + os: macOS-latest arch: auto - - python-version: '3.10' - os: windows-latest + - python-version: '3.12' install-extras: tests-strict,runtime-strict,optional-strict,headless-strict + os: windows-latest arch: auto - - python-version: '3.10' - os: macOS-latest + - python-version: '3.12' install-extras: tests,headless - arch: auto - - python-version: '3.10' os: windows-latest - install-extras: tests,headless arch: auto - - python-version: '3.7' - os: ubuntu-latest - install-extras: tests,optional,headless + - python-version: '3.12' + install-extras: tests,headless + os: windows-latest arch: auto - python-version: '3.8' - os: ubuntu-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.9' - os: ubuntu-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.10' - os: ubuntu-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - - python-version: '3.7' - os: macOS-latest + - python-version: '3.11' + install-extras: tests,optional,headless + os: windows-latest + arch: auto + - python-version: '3.12' install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.8' - os: macOS-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.9' - os: macOS-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.10' - os: macOS-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - - python-version: '3.7' + - python-version: '3.11' + install-extras: tests,optional,headless os: windows-latest + arch: auto + - python-version: '3.12' install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.8' - os: windows-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.9' - os: windows-latest install-extras: tests,optional,headless + os: windows-latest arch: auto - python-version: '3.10' + install-extras: tests,optional,headless os: windows-latest + arch: auto + - python-version: '3.11' + install-extras: tests,optional,headless + os: windows-latest + arch: auto + - python-version: '3.12' install-extras: tests,optional,headless + os: windows-latest arch: auto steps: - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.1 - name: Enable MSVC 64bit uses: ilammy/msvc-dev-cmd@v1 - if: matrix.os == 'windows-latest' && matrix.cibw_skip == '*-win32' - - name: Enable MSVC 32bit - uses: ilammy/msvc-dev-cmd@v1 - if: matrix.os == 'windows-latest' && matrix.cibw_skip == '*-win_amd64' - with: - arch: x86 + if: matrix.os == 'windows-latest' - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 if: runner.os == 'Linux' && matrix.arch != 'auto' with: platforms: all - name: Setup Python - uses: actions/setup-python@v4.5.0 + uses: actions/setup-python@v5.0.0 with: python-version: ${{ matrix.python-version }} - - name: Build pure wheel + - uses: actions/download-artifact@v2.1.1 + name: Download wheels + with: + name: wheels + path: wheelhouse + - name: Install wheel ${{ matrix.install-extras }} shell: bash + env: + INSTALL_EXTRAS: ${{ matrix.install-extras }} run: |- - python -m pip install pip -U - python -m pip install setuptools>=0.8 build - python -m build --wheel --outdir wheelhouse - - name: Test wheel with ${{ matrix.install-extras }} + echo "Finding the path to the wheel" + ls wheelhouse || echo "wheelhouse does not exist" + echo "Installing helpers" + pip install setuptools>=0.8 setuptools_scm wheel build -U + pip install tomli pkginfo + export WHEEL_FPATH=$(python -c "import pathlib; print(str(sorted(pathlib.Path('wheelhouse').glob('plottool_ibeis*.whl'))[-1]).replace(chr(92), chr(47)))") + export MOD_VERSION=$(python -c "from pkginfo import Wheel; print(Wheel('$WHEEL_FPATH').version)") + echo "$WHEEL_FPATH=WHEEL_FPATH" + echo "$INSTALL_EXTRAS=INSTALL_EXTRAS" + echo "$MOD_VERSION=MOD_VERSION" + pip install --prefer-binary "plottool_ibeis[$INSTALL_EXTRAS]==$MOD_VERSION" -f wheelhouse + echo "Install finished." + - name: Test wheel ${{ matrix.install-extras }} shell: bash env: - INSTALL_EXTRAS: ${{ matrix.install-extras }} CI_PYTHON_VERSION: py${{ matrix.python-version }} run: |- - # Find the path to the wheel - ls wheelhouse - pip install tomli pkginfo - MOD_NAME=plottool_ibeis - echo "MOD_NAME=$MOD_NAME" - WHEEL_FPATH=$(python -c "import pathlib; print(str(sorted(pathlib.Path('wheelhouse').glob('$MOD_NAME*.whl'))[-1]).replace(chr(92), chr(47)))") - echo "WHEEL_FPATH=$WHEEL_FPATH" - MOD_VERSION=$(python -c "from pkginfo import Wheel; print(Wheel('$WHEEL_FPATH').version)") - echo "MOD_VERSION=$MOD_VERSION" - # Install the wheel (ensure we are using the version we just built) - # NOTE: THE VERSION MUST BE NEWER THAN AN EXISTING PYPI VERSION OR THIS MAY FAIL - pip install "$MOD_NAME[$INSTALL_EXTRAS]==$MOD_VERSION" -f wheelhouse - # Create a sandboxed directory - WORKSPACE_DNAME="testdir_${CI_PYTHON_VERSION}_${GITHUB_RUN_ID}_${RUNNER_OS}" + echo "Creating test sandbox directory" + export WORKSPACE_DNAME="testdir_${CI_PYTHON_VERSION}_${GITHUB_RUN_ID}_${RUNNER_OS}" + echo "WORKSPACE_DNAME=$WORKSPACE_DNAME" mkdir -p $WORKSPACE_DNAME + echo "cd-ing into the workspace" cd $WORKSPACE_DNAME + pwd + ls -altr # Get the path to the installed package and run the tests - MOD_DPATH=$(python -c "import plottool_ibeis, os; print(os.path.dirname(plottool_ibeis.__file__))") - echo "MOD_DPATH = $MOD_DPATH" - python -m pytest -p pytester -p no:doctest --xdoctest --cov-config ../pyproject.toml --cov-report term --cov="$MOD_NAME" "$MOD_DPATH" ../tests + export MOD_DPATH=$(python -c "import plottool_ibeis, os; print(os.path.dirname(plottool_ibeis.__file__))") + export MOD_NAME=plottool_ibeis + echo " + --- + MOD_DPATH = $MOD_DPATH + --- + running the pytest command inside the workspace + --- + " + python -m pytest --verbose -p pytester -p no:doctest --xdoctest --cov-config ../pyproject.toml --cov-report term --durations=100 --cov="$MOD_NAME" "$MOD_DPATH" ../tests + echo "pytest command finished, moving the coverage file to the repo root" + ls -al # Move coverage file to a new name mv .coverage "../.coverage.$WORKSPACE_DNAME" + echo "changing directory back to th repo root" cd .. - - name: Show built files - shell: bash - run: ls -la wheelhouse - - name: Set up Python 3.8 to combine coverage Linux - uses: actions/setup-python@v4.5.0 - if: runner.os == 'Linux' - with: - python-version: 3.8 + ls -al - name: Combine coverage Linux if: runner.os == 'Linux' run: |- echo '############ PWD' pwd + cp .wheelhouse/.coverage* . || true ls -al python -m pip install coverage[toml] echo '############ combine' - coverage combine . + coverage combine . || true echo '############ XML' - coverage xml -o ./tests/coverage.xml - echo '############ FIND' - find . -name .coverage.* - find . -name coverage.xml - - uses: codecov/codecov-action@v3 + coverage xml -o ./coverage.xml || true + echo '### The cwd should now have a coverage.xml' + ls -altr + pwd + - uses: codecov/codecov-action@v4.0.1 name: Codecov Upload with: - file: ./tests/coverage.xml - - uses: actions/upload-artifact@v3 - name: Upload wheels artifact - with: - name: wheels - path: ./wheelhouse/plottool_ibeis*.whl + file: ./coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} test_deploy: name: Uploading Test to PyPi runs-on: ubuntu-latest if: github.event_name == 'push' && ! startsWith(github.event.ref, 'refs/tags') && ! startsWith(github.event.ref, 'refs/heads/release') needs: - build_and_test_sdist - - build_and_test_purepy_wheels + - build_purepy_wheels steps: - name: Checkout source - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - name: Download wheels and sdist + uses: actions/checkout@v4.1.1 + - uses: actions/download-artifact@v2.1.1 + name: Download wheels with: name: wheels path: wheelhouse + - uses: actions/download-artifact@v2.1.1 + name: Download sdist + with: + name: sdist_wheels + path: wheelhouse - name: Show files to upload shell: bash run: ls -la wheelhouse - name: Sign and Publish env: TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/ - TWINE_USERNAME: ${{ secrets.TEST_TWINE_USERNAME }} + TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TEST_TWINE_PASSWORD }} CI_SECRET: ${{ secrets.CI_SECRET }} run: |- @@ -307,29 +384,60 @@ jobs: pip install urllib3 requests[security] twine GPG_KEYID=$(cat dev/public_gpg_key) echo "GPG_KEYID = '$GPG_KEYID'" - DO_GPG=True GPG_KEYID=$GPG_KEYID TWINE_REPOSITORY_URL=${TWINE_REPOSITORY_URL} TWINE_PASSWORD=$TWINE_PASSWORD TWINE_USERNAME=$TWINE_USERNAME GPG_EXECUTABLE=$GPG_EXECUTABLE DO_UPLOAD=True DO_TAG=False ./publish.sh + GPG_SIGN_CMD="$GPG_EXECUTABLE --batch --yes --detach-sign --armor --local-user $GPG_KEYID" + WHEEL_PATHS=(wheelhouse/*.whl wheelhouse/*.tar.gz) + WHEEL_PATHS_STR=$(printf '"%s" ' "${WHEEL_PATHS[@]}") + echo "$WHEEL_PATHS_STR" + for WHEEL_PATH in "${WHEEL_PATHS[@]}" + do + echo "------" + echo "WHEEL_PATH = $WHEEL_PATH" + $GPG_SIGN_CMD --output $WHEEL_PATH.asc $WHEEL_PATH + $GPG_EXECUTABLE --verify $WHEEL_PATH.asc $WHEEL_PATH || echo "hack, the first run of gpg very fails" + $GPG_EXECUTABLE --verify $WHEEL_PATH.asc $WHEEL_PATH + done + ls -la wheelhouse + pip install opentimestamps-client + ots stamp wheelhouse/*.whl wheelhouse/*.tar.gz wheelhouse/*.asc + ls -la wheelhouse + twine upload --username __token__ --password "$TWINE_PASSWORD" --repository-url "$TWINE_REPOSITORY_URL" wheelhouse/*.whl wheelhouse/*.tar.gz --skip-existing --verbose || { echo "failed to twine upload" ; exit 1; } + - uses: actions/upload-artifact@v3.1.3 + name: Upload deploy artifacts + with: + name: deploy_artifacts + path: |- + wheelhouse/*.whl + wheelhouse/*.zip + wheelhouse/*.tar.gz + wheelhouse/*.asc + wheelhouse/*.ots live_deploy: name: Uploading Live to PyPi runs-on: ubuntu-latest if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags') || startsWith(github.event.ref, 'refs/heads/release')) needs: - build_and_test_sdist - - build_and_test_purepy_wheels + - build_purepy_wheels steps: - name: Checkout source - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - name: Download wheels and sdist + uses: actions/checkout@v4.1.1 + - uses: actions/download-artifact@v2.1.1 + name: Download wheels with: name: wheels path: wheelhouse + - uses: actions/download-artifact@v2.1.1 + name: Download sdist + with: + name: sdist_wheels + path: wheelhouse - name: Show files to upload shell: bash run: ls -la wheelhouse - name: Sign and Publish env: TWINE_REPOSITORY_URL: https://upload.pypi.org/legacy/ - TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} + TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} CI_SECRET: ${{ secrets.CI_SECRET }} run: |- @@ -350,7 +458,78 @@ jobs: pip install urllib3 requests[security] twine GPG_KEYID=$(cat dev/public_gpg_key) echo "GPG_KEYID = '$GPG_KEYID'" - DO_GPG=True GPG_KEYID=$GPG_KEYID TWINE_REPOSITORY_URL=${TWINE_REPOSITORY_URL} TWINE_PASSWORD=$TWINE_PASSWORD TWINE_USERNAME=$TWINE_USERNAME GPG_EXECUTABLE=$GPG_EXECUTABLE DO_UPLOAD=True DO_TAG=False ./publish.sh + GPG_SIGN_CMD="$GPG_EXECUTABLE --batch --yes --detach-sign --armor --local-user $GPG_KEYID" + WHEEL_PATHS=(wheelhouse/*.whl wheelhouse/*.tar.gz) + WHEEL_PATHS_STR=$(printf '"%s" ' "${WHEEL_PATHS[@]}") + echo "$WHEEL_PATHS_STR" + for WHEEL_PATH in "${WHEEL_PATHS[@]}" + do + echo "------" + echo "WHEEL_PATH = $WHEEL_PATH" + $GPG_SIGN_CMD --output $WHEEL_PATH.asc $WHEEL_PATH + $GPG_EXECUTABLE --verify $WHEEL_PATH.asc $WHEEL_PATH || echo "hack, the first run of gpg very fails" + $GPG_EXECUTABLE --verify $WHEEL_PATH.asc $WHEEL_PATH + done + ls -la wheelhouse + pip install opentimestamps-client + ots stamp wheelhouse/*.whl wheelhouse/*.tar.gz wheelhouse/*.asc + ls -la wheelhouse + twine upload --username __token__ --password "$TWINE_PASSWORD" --repository-url "$TWINE_REPOSITORY_URL" wheelhouse/*.whl wheelhouse/*.tar.gz --skip-existing --verbose || { echo "failed to twine upload" ; exit 1; } + - uses: actions/upload-artifact@v3.1.3 + name: Upload deploy artifacts + with: + name: deploy_artifacts + path: |- + wheelhouse/*.whl + wheelhouse/*.zip + wheelhouse/*.tar.gz + wheelhouse/*.asc + wheelhouse/*.ots + release: + name: Create Github Release + if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags') || startsWith(github.event.ref, 'refs/heads/release')) + runs-on: ubuntu-latest + permissions: + contents: write + needs: + - live_deploy + steps: + - name: Checkout source + uses: actions/checkout@v4.1.1 + - uses: actions/download-artifact@v2.1.1 + name: Download artifacts + with: + name: deploy_artifacts + path: wheelhouse + - name: Show files to release + shell: bash + run: ls -la wheelhouse + - run: 'echo "Automatic Release Notes. TODO: improve" > ${{ github.workspace }}-CHANGELOG.txt' + - name: Tag Release Commit + if: (startsWith(github.event.ref, 'refs/heads/release')) + run: |- + export VERSION=$(python -c "import setup; print(setup.VERSION)") + git tag "v$VERSION" + git push origin "v$VERSION" + - uses: softprops/action-gh-release@v1 + name: Create Release + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + body_path: ${{ github.workspace }}-CHANGELOG.txt + tag_name: ${{ github.ref }} + name: Release ${{ github.ref }} + body: Automatic Release + generate_release_notes: true + draft: true + prerelease: false + files: |- + wheelhouse/*.whl + wheelhouse/*.asc + wheelhouse/*.ots + wheelhouse/*.zip + wheelhouse/*.tar.gz ### diff --git a/.readthedocs.yml b/.readthedocs.yml index adc3453..37de119 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,11 +7,14 @@ # Required version: 2 +build: + os: "ubuntu-22.04" + tools: + python: "3.11" sphinx: configuration: docs/source/conf.py formats: all python: - version: 3.7 install: - requirements: requirements/headless.txt - requirements: requirements/docs.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 745da6e..3165ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,25 @@ We are currently working on porting this changelog to the specifications in [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Version 2.2.1] - + +## [Version 2.2.3] - + +### Changed +* Replaced some utool calls with ubelt or other equivalents + +### Fixed: +* Removed codecov from test requirements +* Fix type error in chip viz +* Issue with `AxesDivider.append_axes` removing the `add_to_figure` argument + + +## [Version 2.2.2] - + +### Changed +* Add 3.11 support + + +## [Version 2.2.1] - Released 2023-01-28 ### Added * Basic type stubs diff --git a/dev/ci_public_gpg_key.pgp.enc b/dev/ci_public_gpg_key.pgp.enc index 48da8d7..f506e6a 100644 --- a/dev/ci_public_gpg_key.pgp.enc +++ b/dev/ci_public_gpg_key.pgp.enc @@ -1,49 +1,49 @@ -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 -CEW9mgHQKwJrmLORFxezfQ== +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 +O6e3s07J+4GbOKVuCXeMwQ== diff --git a/dev/ci_secret_gpg_subkeys.pgp.enc b/dev/ci_secret_gpg_subkeys.pgp.enc index 53a64c1..fe85f26 100644 --- a/dev/ci_secret_gpg_subkeys.pgp.enc +++ b/dev/ci_secret_gpg_subkeys.pgp.enc @@ -1,34 +1,34 @@ -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 diff --git a/dev/cleanup_utool.py b/dev/cleanup_utool.py new file mode 100644 index 0000000..7c07754 --- /dev/null +++ b/dev/cleanup_utool.py @@ -0,0 +1,24 @@ +""" +Helper to check what the current usage of utool in the project is as we slowly +remove it. + +SeeAlso: + ~/code/ubelt/dev/oneoff/remove_ancient_constructs.py +""" +import xdev +import plottool_ibeis +import ubelt as ub +mod_dpath = ub.Path(plottool_ibeis.__file__).parent + +grep_results = xdev.grep('\\but\\.\\w*\\b', dpath=mod_dpath) + +instances = [] + +for result in grep_results: + for line in result.found_lines: + for match in result.pattern.pattern.findall(line): + instances.append(match) + ... + +current_utool_usage = ub.udict(ub.dict_hist(instances)).sorted_values() +print('current_utool_usage = {}'.format(ub.urepr(current_utool_usage, nl=1))) diff --git a/dev/gpg_owner_trust.enc b/dev/gpg_owner_trust.enc index 0083fd3..2ddb686 100644 --- a/dev/gpg_owner_trust.enc +++ b/dev/gpg_owner_trust.enc @@ -1,11 +1,12 @@ -U2FsdGVkX1/++fmJrt46PixYbj3OEXWbjk7rx1Y4drHLN5I0SmCBmiH3U6ZL4zqq -fpBr3ousENwzXOWvmyXsBVapSVCLxLTBexHdzkDA4jGFMXVLvvfOP40T2vzW7BIR -x7N6c+oDexuAPaE/6EcUyPNCLF8KmCHFLSuPVNO8tQS49Dyf9iWrnrhIKW6eAu0L -CwZcDvco1o2AhcJxWfRw5wRa8tXihp2VekNMxuZiVRtA8usVtvWhAqjxKSHTJlGe -af4enBpJGTm/pKil0k9MOMu+VNOsGrM7mY4a0I9YabdeM1N14eDDjGua7YV68WQe -7c9b6JQ3lNV6XnFVVD5e6wLZG8JbpZ8kIx2XLwf5QHPIbQ9PETB/8+3BZK2AYGBD -PYzyAdJU7jZ5BXgXVRjZbAdqFPJhexaHg9ZDEEb4PV9Xngw2q3iESdYt1hPhmUh0 -LaJbq+M6hUtk9l5A++6Nl4LXBNITPKRsCnY8SuaGQSLDqkM1OFuAV39V4xOhfYMY -5lpMd6e5/ABcxFqnhvBb0AKz1clLF6TAEqhLNHhBFYeGzKM/s21IfH0ApBKP2sv2 -VmgQojPQu9GNhn/ZiYJtSJ19pesnzkwrcId+VgiJTqoNwjsjV8gdR2bBFDBimZ7V -C+iaC6YVQF8k4pI8vcB/qQ== +U2FsdGVkX1+Z9ahgalV5GA86MMd/PJ4torUGVxoV4DASdKno2XYIJqlmSTMw0n8V +ugX/p4rJkNyg9V+cekV56/3IaiG2COWqdiOi9PlQeszRUpAvvjIcgXC5ooTyKKES +BXMGGGM1ruxDTj0IMnpAr/Y7w98QNemLlEEk3a+QWyXegNDDmt+CkMvKOwt44bIM +TurP8UYXAPi/nJkGcG4R7hzVKdlLW/gqyv+rkDQxfvjszcmtYQSkFUUGCQfvBujG +a8xZ/8i+edK//QT9Blxds2dS4E9Xvu3+PVrv9EyHFZrTMROH7ROswpBSz5vhUhCX +x/Pf61G9TRogob10Q0jRF1g90oUmHR12nQ+Qn8vRxgLuu06O2Ld+U5e40ZAETewF +O5CoG1F0jSGVQZh1qAN9gdnqushoBx1Yp3gegAvSAjE7E12wsXRKPB0xjflQ5wkL +TicCnX9Ld+FSHbDCvzIaVYLDJIu8HiYAhk4o0S/NfymJxIW7jy6wA3dteqW3mkyi +MB3gPWcJxmZ/YEZ9d8dDXVgE/psJfWx58MEftHmWK0nNWOan6AEjhFVXZ9mFhxhV +L8mOYon5D5VX1qOC07DoJqd9B4g16gHyd+2x1lu8UB/ZL3dXJ+Os/n4+i8GgiWiB +ybYGKe8nfq0ki+CK7AUEBovcedzd0QHQyDspDMsoCHmHLlQjp/at4MsYRayJ4/bC +TMTuiInCNaReTKx7gLKnGw== diff --git a/dev/setup_secrets.sh b/dev/setup_secrets.sh index 1ead971..6321e5a 100644 --- a/dev/setup_secrets.sh +++ b/dev/setup_secrets.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash __doc__=' ============================ SETUP CI SECRET INSTRUCTIONS @@ -25,7 +25,7 @@ following CI platforms is used: GITHUB ACTION INSTRUCTIONS ========================= -* `PERSONAL_GITHUB_PUSH_TOKEN` - +* `PERSONAL_GITHUB_PUSH_TOKEN` - This is only needed if you want to automatically git-tag release branches. To make a API token go to: @@ -47,8 +47,8 @@ GITLAB ACTION INSTRUCTIONS tee /tmp/repl && colordiff .setup_secrets.sh /tmp/repl ``` - * Make sure you add Runners to your project - https://gitlab.org.com/utils/xcookie/-/settings/ci_cd + * Make sure you add Runners to your project + https://gitlab.org.com/utils/xcookie/-/settings/ci_cd in Runners-> Shared Runners and Runners-> Available specific runners @@ -60,16 +60,16 @@ GITLAB ACTION INSTRUCTIONS * TWINE_USERNAME - this is your pypi username twine info is only needed if you want to automatically publish to pypi - * TWINE_PASSWORD - this is your pypi password + * TWINE_PASSWORD - this is your pypi password - * CI_SECRET - We will use this as a secret key to encrypt/decrypt gpg secrets + * CI_SECRET - We will use this as a secret key to encrypt/decrypt gpg secrets This is only needed if you want to automatically sign published wheels with a gpg key. - * GITLAB_ORG_PUSH_TOKEN - + * GITLAB_ORG_PUSH_TOKEN - This is only needed if you want to automatically git-tag release branches. - Create a new personal access token in User->Settings->Tokens, + Create a new personal access token in User->Settings->Tokens, You can name the token GITLAB_ORG_PUSH_TOKEN_VALUE Give it api and write repository permissions @@ -165,8 +165,10 @@ setup_package_environs_github_pyutils(){ upload_github_secrets(){ load_secrets unset GITHUB_TOKEN - #printf "%s" "$GITHUB_TOKEN" | gh auth login --hostname Github.com --with-token - gh auth login + #printf "%s" "$GITHUB_TOKEN" | gh auth login --hostname Github.com --with-token + if ! gh auth status ; then + gh auth login + fi source dev/secrets_configuration.sh gh secret set "TWINE_USERNAME" -b"${!VARNAME_TWINE_USERNAME}" gh secret set "TEST_TWINE_USERNAME" -b"${!VARNAME_TEST_TWINE_USERNAME}" @@ -223,15 +225,15 @@ upload_gitlab_group_secrets(){ TMP_DIR=$(mktemp -d -t ci-XXXXXXXXXX) curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups" > "$TMP_DIR/all_group_info" - GROUP_ID=$(cat "$TMP_DIR/all_group_info" | jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") + GROUP_ID=$(< "$TMP_DIR/all_group_info" jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") echo "GROUP_ID = $GROUP_ID" curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups/$GROUP_ID" > "$TMP_DIR/group_info" - cat "$TMP_DIR/group_info" | jq + < "$TMP_DIR/group_info" jq # Get group-level secret variables curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups/$GROUP_ID/variables" > "$TMP_DIR/group_vars" - cat "$TMP_DIR/group_vars" | jq '.[] | .key' + < "$TMP_DIR/group_vars" jq '.[] | .key' if [[ "$?" != "0" ]]; then echo "Failed to access group level variables. Probably a permission issue" @@ -244,7 +246,7 @@ upload_gitlab_group_secrets(){ echo "" echo " ---- " LOCAL_VALUE=${!SECRET_VARNAME} - REMOTE_VALUE=$(cat "$TMP_DIR/group_vars" | jq -r ".[] | select(.key==\"$SECRET_VARNAME\") | .value") + REMOTE_VALUE=$(< "$TMP_DIR/group_vars" jq -r ".[] | select(.key==\"$SECRET_VARNAME\") | .value") # Print current local and remote value of a variable echo "SECRET_VARNAME_PTR = $SECRET_VARNAME_PTR" @@ -264,14 +266,14 @@ upload_gitlab_group_secrets(){ --form "protected=true" \ --form "masked=true" \ --form "environment_scope=*" \ - --form "variable_type=env_var" + --form "variable_type=env_var" toggle_setx_exit elif [[ "$REMOTE_VALUE" != "$LOCAL_VALUE" ]]; then echo "Remove variable does not agree, putting" # Update variable value toggle_setx_enter curl --request PUT --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups/$GROUP_ID/variables/$SECRET_VARNAME" \ - --form "value=${LOCAL_VALUE}" + --form "value=${LOCAL_VALUE}" toggle_setx_exit else echo "Remote value agrees with local" @@ -305,23 +307,23 @@ upload_gitlab_repo_secrets(){ toggle_setx_enter curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups" > "$TMP_DIR/all_group_info" toggle_setx_exit - GROUP_ID=$(cat "$TMP_DIR/all_group_info" | jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") + GROUP_ID=$(< "$TMP_DIR/all_group_info" jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") echo "GROUP_ID = $GROUP_ID" toggle_setx_enter curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups/$GROUP_ID" > "$TMP_DIR/group_info" toggle_setx_exit - GROUP_ID=$(cat "$TMP_DIR/all_group_info" | jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") - cat "$TMP_DIR/group_info" | jq + GROUP_ID=$(< "$TMP_DIR/all_group_info" jq ". | map(select(.path==\"$GROUP_NAME\")) | .[0].id") + < "$TMP_DIR/group_info" jq - PROJECT_ID=$(cat "$TMP_DIR/group_info" | jq ".projects | map(select(.path==\"$PROJECT_NAME\")) | .[0].id") + PROJECT_ID=$(< "$TMP_DIR/group_info" jq ".projects | map(select(.path==\"$PROJECT_NAME\")) | .[0].id") echo "PROJECT_ID = $PROJECT_ID" # Get group-level secret variables toggle_setx_enter curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/projects/$PROJECT_ID/variables" > "$TMP_DIR/project_vars" toggle_setx_exit - cat "$TMP_DIR/project_vars" | jq '.[] | .key' + < "$TMP_DIR/project_vars" jq '.[] | .key' if [[ "$?" != "0" ]]; then echo "Failed to access project level variables. Probably a permission issue" fi @@ -334,7 +336,7 @@ upload_gitlab_repo_secrets(){ echo "" echo " ---- " LOCAL_VALUE=${!SECRET_VARNAME} - REMOTE_VALUE=$(cat "$TMP_DIR/project_vars" | jq -r ".[] | select(.key==\"$SECRET_VARNAME\") | .value") + REMOTE_VALUE=$(< "$TMP_DIR/project_vars" jq -r ".[] | select(.key==\"$SECRET_VARNAME\") | .value") # Print current local and remote value of a variable echo "SECRET_VARNAME_PTR = $SECRET_VARNAME_PTR" @@ -353,7 +355,7 @@ upload_gitlab_repo_secrets(){ --form "protected=true" \ --form "masked=true" \ --form "environment_scope=*" \ - --form "variable_type=env_var" + --form "variable_type=env_var" else echo "dry run, not posting" fi @@ -362,7 +364,7 @@ upload_gitlab_repo_secrets(){ # Update variable value if [[ "$LIVE_MODE" == "1" ]]; then curl --request PUT --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/projects/$PROJECT_ID/variables/$SECRET_VARNAME" \ - --form "value=${LOCAL_VALUE}" + --form "value=${LOCAL_VALUE}" else echo "dry run, not putting" fi @@ -405,7 +407,7 @@ export_encrypted_code_signing_keys(){ echo "MAIN_GPG_KEYID = $MAIN_GPG_KEYID" echo "GPG_SIGN_SUBKEY = $GPG_SIGN_SUBKEY" - # Only export the signing secret subkey + # Only export the signing secret subkey # Export plaintext gpg public keys, private sign key, and trust info mkdir -p dev gpg --armor --export-options export-backup --export-secret-subkeys "${GPG_SIGN_SUBKEY}!" > dev/ci_secret_gpg_subkeys.pgp @@ -421,7 +423,7 @@ export_encrypted_code_signing_keys(){ # Test decrpyt GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_public_gpg_key.pgp.enc | gpg --list-packets --verbose GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_secret_gpg_subkeys.pgp.enc | gpg --list-packets --verbose - GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/gpg_owner_trust.enc + GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/gpg_owner_trust.enc cat dev/public_gpg_key unload_secrets @@ -451,14 +453,14 @@ _test_gnu(){ source dev/secrets_configuration.sh gpg -k - + load_secrets CI_SECRET="${!VARNAME_CI_SECRET}" echo "CI_SECRET = $CI_SECRET" cat dev/public_gpg_key - GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_public_gpg_key.pgp.enc - GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/gpg_owner_trust.enc + GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_public_gpg_key.pgp.enc + GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/gpg_owner_trust.enc GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_secret_gpg_subkeys.pgp.enc GLKWS=$CI_SECRET openssl enc -aes-256-cbc -pbkdf2 -md SHA512 -pass env:GLKWS -d -a -in dev/ci_public_gpg_key.pgp.enc | gpg --import diff --git a/docs/source/conf.py b/docs/source/conf.py index 04536cd..8fc5052 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,11 +1,15 @@ """ Notes: + Based on template code in: + ~/code/xcookie/xcookie/builders/docs.py + ~/code/xcookie/xcookie/rc/conf_ext.py + http://docs.readthedocs.io/en/latest/getting_started.html pip install sphinx sphinx-autobuild sphinx_rtd_theme sphinxcontrib-napoleon cd ~/code/plottool_ibeis - mkdir docs + mkdir -p docs cd docs sphinx-quickstart @@ -13,37 +17,79 @@ # need to edit the conf.py cd ~/code/plottool_ibeis/docs - sphinx-apidoc -f -o ~/code/plottool_ibeis/docs/source ~/code/plottool_ibeis/plottool_ibeis --separate + sphinx-apidoc --private --separate -f -o ~/code/plottool_ibeis/docs/source/auto ~/code/plottool_ibeis/plottool_ibeis + + # Note: the module should importable before running this + # (e.g. install it in developer mode or munge the PYTHONPATH) make html + git add source/auto/*.rst + Also: To turn on PR checks https://docs.readthedocs.io/en/stable/guides/autobuild-docs-for-pull-requests.html - https://readthedocs.org/dashboard/plottool_ibeis/advanced/ + https://readthedocs.org/dashboard/plottool-ibeis/advanced/ ensure your github account is connected to readthedocs https://readthedocs.org/accounts/social/connections/ ### For gitlab + To enable the read-the-docs go to https://readthedocs.org/dashboard/ and login + The user will need to enable the repo on their readthedocs account: https://readthedocs.org/dashboard/import/manual/? - To enable the read-the-docs go to https://readthedocs.org/dashboard/ and login + Enter the following information: + Set the Repository NAME: plottool_ibeis + Set the Repository URL: https://github.com/Erotemic/plottool_ibeis Make sure you have a .readthedocs.yml file - Click import project: (for github you can select, but gitlab you need to import manually) - Set the Repository NAME: $REPO_NAME - Set the Repository URL: $REPO_URL + For gitlab you also need to setup an integrations. Navigate to: + + https://readthedocs.org/dashboard/plottool-ibeis/integrations/create/ + + Then add gitlab incoming webhook and copy the URL (make sure + you copy the real url and not the text so https is included), + specifically: + + In the "Integration type:" dropdown menu, select + "Gitlab incoming webhook" + + Click "Add integration" + + Copy the text in the "Webhook URL" box to be used later. + + Copy the text in the "Secret" box to be used later. + + Then go to + + https://github.com/Erotemic/plottool_ibeis/hooks + + Click "Add new webhook". + + Copy the text previously saved from the "Webhook URL" box + in the readthedocs form into the "URL" box in the gitlab + form. + + Copy the text previously saved from the "Secret" box + in the readthedocs form into the "Secret token" box in the + gitlab form. + + For trigger permissions select the following checkboxes: + push events, + tag push events, + merge request events + + Click the "Add webhook" button. - For gitlab you also need to setup an integrations and add gitlab - incoming webhook Then go to $REPO_URL/hooks and add the URL + See Docs for more details https://docs.readthedocs.io/en/stable/integrations.html Will also need to activate the main branch: - https://readthedocs.org/projects/plottool_ibeis/versions/ + https://readthedocs.org/projects/plottool-ibeis/versions/ """ # # Configuration file for the Sphinx documentation builder. @@ -90,14 +136,19 @@ def visit_Assign(self, node): return visitor.version project = 'plottool_ibeis' -copyright = '2022, Jon Crall' +copyright = '2024, Jon Crall' author = 'Jon Crall' modname = 'plottool_ibeis' -modpath = join(dirname(dirname(dirname(__file__))), modname, '__init__.py') +repo_dpath = dirname(dirname(dirname(__file__))) +mod_dpath = join(repo_dpath, 'plottool_ibeis') +src_dpath = dirname(mod_dpath) +modpath = join(mod_dpath, '__init__.py') release = parse_version(modpath) version = '.'.join(release.split('.')[0:2]) +# Hack to ensure the module is importable +# sys.path.insert(0, os.path.abspath(src_dpath)) # -- General configuration --------------------------------------------------- @@ -109,13 +160,18 @@ def visit_Assign(self, node): # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + # 'autoapi.extension', 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', + 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', + 'sphinx.ext.napoleon', 'sphinx.ext.todo', - 'sphinx.ext.autosummary', - # 'myst_parser', # TODO + 'sphinx.ext.viewcode', + 'myst_parser', # For markdown docs + 'sphinx.ext.imgconverter', # For building latexpdf + 'sphinx.ext.githubpages', + # 'sphinxcontrib.redirects', + 'sphinx_reredirects', ] todo_include_todos = True @@ -123,11 +179,37 @@ def visit_Assign(self, node): napoleon_use_param = False napoleon_use_ivar = True +#autoapi_type = 'python' +#autoapi_dirs = [mod_dpath] + autodoc_inherit_docstrings = False +# Hack for geowatch, todo configure +autosummary_mock_imports = [ + 'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_24_and_lt_4_xx', + 'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_22_and_lt_4_24', + 'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_21_and_lt_4_22', + 'geowatch.tasks.fusion.datamodules.temporal_sampling.affinity_sampling', + 'geowatch.tasks.depth_pcd.model', + 'geowatch.tasks.cold.export_change_map', +] + autodoc_member_order = 'bysource' +autoclass_content = 'both' # autodoc_mock_imports = ['torch', 'torchvision', 'visdom'] +# autoapi_modules = { +# modname: { +# 'override': False, +# 'output': 'auto' +# } +# } +# autoapi_dirs = [f'../../src/{modname}'] +# autoapi_keep_files = True + +# References: +# https://stackoverflow.com/questions/21538983/specifying-targets-for-intersphinx-links-to-numpy-scipy-and-matplotlib + intersphinx_mapping = { # 'pytorch': ('http://pytorch.org/docs/master/', None), 'python': ('https://docs.python.org/3', None), @@ -144,7 +226,24 @@ def visit_Assign(self, node): 'xdoctest': ('https://xdoctest.readthedocs.io/en/latest/', None), 'networkx': ('https://networkx.org/documentation/stable/', None), 'scriptconfig': ('https://scriptconfig.readthedocs.io/en/latest/', None), - + 'rich': ('https://rich.readthedocs.io/en/latest/', None), + + 'numpy': ('https://numpy.org/doc/stable/', None), + 'sympy': ('https://docs.sympy.org/latest/', None), + 'scikit-learn': ('https://scikit-learn.org/stable/', None), + 'pandas': ('https://pandas.pydata.org/docs/', None), + 'matplotlib': ('https://matplotlib.org/stable/', None), + + 'pytest': ('https://docs.pytest.org/en/latest/', None), + 'platformdirs': ('https://platformdirs.readthedocs.io/en/latest/', None), + + 'timerit': ('https://timerit.readthedocs.io/en/latest/', None), + 'progiter': ('https://progiter.readthedocs.io/en/latest/', None), + 'dateutil': ('https://dateutil.readthedocs.io/en/latest/', None), + # 'pytest._pytest.doctest': ('https://docs.pytest.org/en/latest/_modules/_pytest/doctest.html', None), + # 'colorama': ('https://pypi.org/project/colorama/', None), + # 'cv2' : ('http://docs.opencv.org/2.4/', None), + # 'h5py' : ('http://docs.h5py.org/en/latest/', None) } __dev_note__ = """ python -m sphinx.ext.intersphinx https://docs.python.org/3/objects.inv @@ -154,6 +253,11 @@ def visit_Assign(self, node): python -m sphinx.ext.intersphinx https://kwimage.readthedocs.io/en/latest/objects.inv python -m sphinx.ext.intersphinx https://ubelt.readthedocs.io/en/latest/objects.inv python -m sphinx.ext.intersphinx https://networkx.org/documentation/stable/objects.inv + +sphobjinv suggest -t 90 -u https://readthedocs.org/projects/pytest/reference/objects.inv +"signal.convolve2d" + +python -m sphinx.ext.intersphinx https://pygments-doc.readthedocs.io/en/latest/objects.inv """ @@ -199,6 +303,7 @@ def visit_Assign(self, node): html_theme_options = { 'collapse_navigation': False, 'display_version': True, + 'navigation_depth': -1, # 'logo_only': True, } # html_logo = '.static/plottool_ibeis.svg' @@ -223,11 +328,26 @@ def visit_Assign(self, node): # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'plottool_ibeisdoc' +htmlhelp_basename = project + 'doc' # -- Options for LaTeX output ------------------------------------------------ +# References: +# https://tex.stackexchange.com/questions/546246/centos-8-the-font-freeserif-cannot-be-found + +""" +# https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.latex.LaTeXBuilder +# https://tex.stackexchange.com/a/570691/83399 +sudo apt install fonts-freefont-otf texlive-luatex texlive-latex-extra texlive-fonts-recommended texlive-latex-recommended tex-gyre latexmk +make latexpdf LATEXMKOPTS="-shell-escape --synctex=-1 -src-specials -interaction=nonstopmode" +make latexpdf LATEXMKOPTS="-lualatex -interaction=nonstopmode" +make LATEXMKOPTS="-lualatex -interaction=nonstopmode" + +""" +# latex_engine = 'lualatex' +# latex_engine = 'xelatex' + latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # @@ -278,116 +398,636 @@ def visit_Assign(self, node): # -- Extension configuration ------------------------------------------------- - - from sphinx.domains.python import PythonDomain # NOQA # from sphinx.application import Sphinx # NOQA from typing import Any, List # NOQA +# HACK TO PREVENT EXCESSIVE TIME. +# TODO: FIXME FOR REAL +MAX_TIME_MINUTES = None +if MAX_TIME_MINUTES: + import ubelt # NOQA + TIMER = ubelt.Timer() + TIMER.tic() + + class PatchedPythonDomain(PythonDomain): """ References: https://github.com/sphinx-doc/sphinx/issues/3866 """ def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - # TODO: can use this to resolve references nicely - # if target.startswith('ub.'): - # target = 'ubelt.' + target[3] + """ + Helps to resolves cross-references + """ + if target.startswith('ub.'): + target = 'ubelt.' + target[3] + if target.startswith('xdoc.'): + target = 'xdoctest.' + target[3] return_value = super(PatchedPythonDomain, self).resolve_xref( env, fromdocname, builder, typ, target, node, contnode) return return_value -def process(app, what_: str, name: str, obj: Any, options: Any, lines: - List[str]) -> None: +class GoogleStyleDocstringProcessor: + """ + A small extension that runs after napoleon and reformats erotemic-flavored + google-style docstrings for sphinx. + """ + + def __init__(self, autobuild=1): + self.debug = 0 + self.registry = {} + if autobuild: + self._register_builtins() + + def register_section(self, tag, alias=None): + """ + Decorator that adds a custom processing function for a non-standard + google style tag. The decorated function should accept a list of + docstring lines, where the first one will be the google-style tag that + likely needs to be replaced, and then return the appropriate sphinx + format (TODO what is the name? Is it just RST?). + """ + alias = [] if alias is None else alias + alias = [alias] if not isinstance(alias, (list, tuple, set)) else alias + alias.append(tag) + alias = tuple(alias) + # TODO: better tag patterns + def _wrap(func): + self.registry[tag] = { + 'tag': tag, + 'alias': alias, + 'func': func, + } + return func + return _wrap + + def _register_builtins(self): + """ + Adds definitions I like of CommandLine, TextArt, and Ignore + """ + + @self.register_section(tag='CommandLine') + def commandline(lines): + new_lines = [] + new_lines.append('.. rubric:: CommandLine') + new_lines.append('') + new_lines.append('.. code-block:: bash') + new_lines.append('') + new_lines.extend(lines[1:]) + return new_lines + + @self.register_section(tag='SpecialExample', alias=['Benchmark', 'Sympy', 'Doctest']) + def benchmark(lines): + import textwrap + new_lines = [] + tag = lines[0].replace(':', '').strip() + # new_lines.append(lines[0]) # TODO: it would be nice to change the tagline. + # new_lines.append('') + new_lines.append('.. rubric:: {}'.format(tag)) + new_lines.append('') + new_text = textwrap.dedent('\n'.join(lines[1:])) + redone = new_text.split('\n') + new_lines.extend(redone) + # import ubelt as ub + # print('new_lines = {}'.format(ub.urepr(new_lines, nl=1))) + # new_lines.append('') + return new_lines + + @self.register_section(tag='TextArt', alias=['Ascii']) + def text_art(lines): + new_lines = [] + new_lines.append('.. rubric:: TextArt') + new_lines.append('') + new_lines.append('.. code-block:: bash') + new_lines.append('') + new_lines.extend(lines[1:]) + return new_lines + + # @self.register_section(tag='TODO', alias=['.. todo::']) + # def todo_section(lines): + # """ + # Fixup todo sections + # """ + # import xdev + # xdev.embed() + # import ubelt as ub + # print('lines = {}'.format(ub.urepr(lines, nl=1))) + # return new_lines + + @self.register_section(tag='Ignore') + def ignore(lines): + return [] + + def process(self, lines): + """ + Example: + >>> import ubelt as ub + >>> self = GoogleStyleDocstringProcessor() + >>> lines = ['Hello world', + >>> '', + >>> 'CommandLine:', + >>> ' hi', + >>> '', + >>> 'CommandLine:', + >>> '', + >>> ' bye', + >>> '', + >>> 'TextArt:', + >>> '', + >>> ' 1', + >>> ' 2', + >>> '', + >>> ' 345', + >>> '', + >>> 'Foobar:', + >>> '', + >>> 'TextArt:'] + >>> new_lines = self.process(lines[:]) + >>> print(chr(10).join(new_lines)) + """ + orig_lines = lines[:] + new_lines = [] + curr_mode = '__doc__' + accum = [] + + def accept(): + """ called when we finish reading a section """ + if curr_mode == '__doc__': + # Keep the lines as-is + new_lines.extend(accum) + else: + # Process this section with the given function + regitem = self.registry[curr_mode] + func = regitem['func'] + fixed = func(accum) + new_lines.extend(fixed) + # Reset the accumulator for the next section + accum[:] = [] + + for line in orig_lines: + + found = None + for regitem in self.registry.values(): + if line.startswith(regitem['alias']): + found = regitem['tag'] + break + if not found and line and not line.startswith(' '): + # if the line startswith anything but a space, we are no longer + # in the previous nested scope. NOTE: This assumption may not + # be general, but it works for my code. + found = '__doc__' + + if found: + # New section is found, accept the previous one and start + # accumulating the new one. + accept() + curr_mode = found + + accum.append(line) + + # Finialize the last section + accept() + + lines[:] = new_lines + # make sure there is a blank line at the end + if lines and lines[-1]: + lines.append('') + + return lines + + def process_docstring_callback(self, app, what_: str, name: str, obj: Any, + options: Any, lines: List[str]) -> None: + """ + Callback to be registered to autodoc-process-docstring + + Custom process to transform docstring lines Remove "Ignore" blocks + + Args: + app (sphinx.application.Sphinx): the Sphinx application object + + what (str): + the type of the object which the docstring belongs to (one of + "module", "class", "exception", "function", "method", "attribute") + + name (str): the fully qualified name of the object + + obj: the object itself + + options: the options given to the directive: an object with + attributes inherited_members, undoc_members, show_inheritance + and noindex that are true if the flag option of same name was + given to the auto directive + + lines (List[str]): the lines of the docstring, see above + + References: + https://www.sphinx-doc.org/en/1.5.1/_modules/sphinx/ext/autodoc.html + https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + """ + if self.debug: + print(f'ProcessDocstring: name={name}, what_={what_}, num_lines={len(lines)}') + + # print('BEFORE:') + # import ubelt as ub + # print('lines = {}'.format(ub.urepr(lines, nl=1))) + + self.process(lines) + + # docstr = '\n'.join(lines) + # if 'Convert the Mask' in docstr: + # import xdev + # xdev.embed() + + # if 'keys in this dictionary ' in docstr: + # import xdev + # xdev.embed() + + render_doc_images = 0 + + if MAX_TIME_MINUTES and TIMER.toc() > (60 * MAX_TIME_MINUTES): + render_doc_images = False # FIXME too slow on RTD + + if render_doc_images: + # DEVELOPING + if any('REQUIRES(--show)' in line for line in lines): + # import xdev + # xdev.embed() + create_doctest_figure(app, obj, name, lines) + + FIX_EXAMPLE_FORMATTING = 1 + if FIX_EXAMPLE_FORMATTING: + for idx, line in enumerate(lines): + if line == "Example:": + lines[idx] = "**Example:**" + lines.insert(idx + 1, "") + + REFORMAT_SECTIONS = 0 + if REFORMAT_SECTIONS: + REFORMAT_RETURNS = 0 + REFORMAT_PARAMS = 0 + + docstr = SphinxDocstring(lines) + + if REFORMAT_PARAMS: + for found in docstr.find_tagged_lines('Parameters'): + print(found['text']) + edit_slice = found['edit_slice'] + + # TODO: figure out how to do this. + + # # file = 'foo.rst' + # import rstparse + # rst = rstparse.Parser() + # import io + # rst.read(io.StringIO(found['text'])) + # rst.parse() + # for line in rst.lines: + # print(line) + + # # found['text'] + # import docutils + + # settings = docutils.frontend.OptionParser( + # components=(docutils.parsers.rst.Parser,) + # ).get_default_values() + # document = docutils.utils.new_document('', settings) + # from docutils.parsers import rst + # rst.Parser().parse(found['text'], document) + + if REFORMAT_RETURNS: + for found in docstr.find_tagged_lines('returns'): + # FIXME: account for new slice with -2 offset + edit_slice = found['edit_slice'] + text = found['text'] + new_lines = [] + for para in text.split('\n\n'): + indent = para[:len(para) - len(para.lstrip())] + new_paragraph = indent + paragraph(para) + new_lines.append(new_paragraph) + new_lines.append('') + new_lines = new_lines[:-1] + lines[edit_slice] = new_lines + + # print('AFTER:') + # print('lines = {}'.format(ub.urepr(lines, nl=1))) + + # if name == 'kwimage.Affine.translate': + # import sys + # sys.exit(1) + + +class SphinxDocstring: """ - Custom process to transform docstring lines Remove "Ignore" blocks + Helper to parse and modify sphinx docstrings + """ + def __init__(docstr, lines): + docstr.lines = lines + + # FORMAT THE RETURNS SECTION A BIT NICER + import re + tag_pat = re.compile(r'^:(\w*):') + directive_pat = re.compile(r'^.. (\w*)::\s*(\w*)') + + # Split by sphinx types, mark the line offset where they start / stop + sphinx_parts = [] + for idx, line in enumerate(lines): + tag_match = tag_pat.search(line) + directive_match = directive_pat.search(line) + if tag_match: + tag = tag_match.groups()[0] + sphinx_parts.append({ + 'tag': tag, 'start_offset': idx, + 'type': 'tag', + }) + elif directive_match: + tag = directive_match.groups()[0] + sphinx_parts.append({ + 'tag': tag, 'start_offset': idx, + 'type': 'directive', + }) + + prev_offset = len(lines) + for part in sphinx_parts[::-1]: + part['end_offset'] = prev_offset + prev_offset = part['start_offset'] + + docstr.sphinx_parts = sphinx_parts + + if 0: + for line in lines: + print(line) + + def find_tagged_lines(docstr, tag): + for part in docstr.sphinx_parts[::-1]: + if part['tag'] == tag: + edit_slice = slice(part['start_offset'], part['end_offset']) + return_section = docstr.lines[edit_slice] + text = '\n'.join(return_section) + found = { + 'edit_slice': edit_slice, + 'text': text, + } + yield found + + +def paragraph(text): + r""" + Wraps multi-line strings and restructures the text to remove all newlines, + heading, trailing, and double spaces. + + Useful for writing log messages Args: - app (sphinx.application.Sphinx): the Sphinx application object + text (str): typically a multiline string - what (str): - the type of the object which the docstring belongs to (one of - "module", "class", "exception", "function", "method", "attribute") + Returns: + str: the reduced text block + """ + import re + out = re.sub(r'\s\s*', ' ', text).strip() + return out - name (str): the fully qualified name of the object - obj: the object itself +def create_doctest_figure(app, obj, name, lines): + """ + The idea is that each doctest that produces a figure should generate that + and then that figure should be part of the docs. + """ + import xdoctest + import sys + import types + if isinstance(obj, types.ModuleType): + module = obj + else: + module = sys.modules[obj.__module__] + # TODO: read settings from pyproject.toml? + if '--show' not in sys.argv: + sys.argv.append('--show') + if '--nointeract' not in sys.argv: + sys.argv.append('--nointeract') + modpath = module.__file__ + + # print(doctest.format_src()) + import pathlib + # HACK: write to the srcdir + doc_outdir = pathlib.Path(app.outdir) + doc_srcdir = pathlib.Path(app.srcdir) + doc_static_outdir = doc_outdir / '_static' + doc_static_srcdir = doc_srcdir / '_static' + src_fig_dpath = (doc_static_srcdir / 'images') + src_fig_dpath.mkdir(exist_ok=True, parents=True) + out_fig_dpath = (doc_static_outdir / 'images') + out_fig_dpath.mkdir(exist_ok=True, parents=True) + + # fig_dpath = (doc_outdir / 'autofigs' / name).mkdir(exist_ok=True) + + fig_num = 1 + + import kwplot + kwplot.autompl(force='agg') + plt = kwplot.autoplt() + + docstr = '\n'.join(lines) + + # TODO: The freeform parser does not work correctly here. + # We need to parse out the sphinx (epdoc)? individual examples + # so we can get different figures. But we can hack it for now. + + import re + split_parts = re.split('({}\\s*\n)'.format(re.escape('.. rubric:: Example')), docstr) + # split_parts = docstr.split('.. rubric:: Example') + + # import xdev + # xdev.embed() + + def doctest_line_offsets(doctest): + # Where the doctests starts and ends relative to the file + start_line_offset = doctest.lineno - 1 + last_part = doctest._parts[-1] + last_line_offset = start_line_offset + last_part.line_offset + last_part.n_lines - 1 + offsets = { + 'start': start_line_offset, + 'end': last_line_offset, + 'stop': last_line_offset + 1, + } + return offsets + + # from xdoctest import utils + # part_lines = utils.add_line_numbers(docstr.split('\n'), n_digits=3, start=0) + # print('\n'.join(part_lines)) + + to_insert_fpaths = [] + curr_line_offset = 0 + for part in split_parts: + num_lines = part.count('\n') + + doctests = list(xdoctest.core.parse_docstr_examples( + part, modpath=modpath, callname=name, + # style='google' + )) + # print(doctests) + + # doctests = list(xdoctest.core.parse_docstr_examples( + # docstr, modpath=modpath, callname=name)) + + for doctest in doctests: + if '--show' in part: + ... + # print('-- SHOW TEST---')/) + # kwplot.close_figures() + try: + import pytest # NOQA + except ImportError: + pass + try: + from xdoctest.exceptions import Skipped + except ImportError: # nocover + # Define dummy skipped exception if pytest is not available + class Skipped(Exception): + pass + try: + doctest.mode = 'native' + doctest.run(verbose=0, on_error='raise') + ... + except Skipped: + print(f'Skip doctest={doctest}') + except Exception as ex: + print(f'ex={ex}') + print(f'Error in doctest={doctest}') + + offsets = doctest_line_offsets(doctest) + doctest_line_end = curr_line_offset + offsets['stop'] + insert_line_index = doctest_line_end + + figures = kwplot.all_figures() + for fig in figures: + fig_num += 1 + # path_name = path_sanatize(name) + path_name = (name).replace('.', '_') + fig_fpath = src_fig_dpath / f'fig_{path_name}_{fig_num:03d}.jpeg' + fig.savefig(fig_fpath) + print(f'Wrote figure: {fig_fpath}') + to_insert_fpaths.append({ + 'insert_line_index': insert_line_index, + 'fpath': fig_fpath, + }) + + for fig in figures: + plt.close(fig) + # kwplot.close_figures(figures) + + curr_line_offset += (num_lines) + + # if len(doctests) > 1: + # doctests + # import xdev + # xdev.embed() - options: the options given to the directive: an object with - attributes inherited_members, undoc_members, show_inheritance - and noindex that are true if the flag option of same name was - given to the auto directive + INSERT_AT = 'end' + INSERT_AT = 'inline' - lines (List[str]): the lines of the docstring, see above + end_index = len(lines) + # Reverse order for inserts + import shutil + for info in to_insert_fpaths[::-1]: + src_abs_fpath = info['fpath'] - References: - https://www.sphinx-doc.org/en/1.5.1/_modules/sphinx/ext/autodoc.html - https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html - """ - # if what and what_ not in what: - # return - orig_lines = lines[:] + rel_to_static_fpath = src_abs_fpath.relative_to(doc_static_srcdir) + # dst_abs_fpath = doc_static_outdir / rel_to_static_fpath + # dst_abs_fpath.parent.mkdir(parents=True, exist_ok=True) - # text = '\n'.join(lines) - # if 'Example' in text and 'CommandLine' in text: - # import xdev - # xdev.embed() + rel_to_root_fpath = src_abs_fpath.relative_to(doc_srcdir) - ignore_tags = tuple(['Ignore']) + dst_abs_fpath1 = doc_outdir / rel_to_root_fpath + dst_abs_fpath1.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src_abs_fpath, dst_abs_fpath1) - mode = None - # buffer = None - new_lines = [] - for i, line in enumerate(orig_lines): - - # See if the line triggers a mode change - if line.startswith(ignore_tags): - mode = 'ignore' - elif line.startswith('CommandLine'): - mode = 'cmdline' - elif line and not line.startswith(' '): - # if the line startswith anything but a space, we are no - # longer in the previous nested scope - mode = None - - if mode is None: - new_lines.append(line) - elif mode == 'ignore': - # print('IGNORE line = {!r}'.format(line)) - pass - elif mode == 'cmdline': - if line.startswith('CommandLine'): - new_lines.append('.. rubric:: CommandLine') - new_lines.append('') - new_lines.append('.. code-block:: bash') - new_lines.append('') - # new_lines.append(' # CommandLine') - else: - # new_lines.append(line.strip()) - new_lines.append(line) + dst_abs_fpath2 = doc_outdir / rel_to_static_fpath + dst_abs_fpath2.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src_abs_fpath, dst_abs_fpath2) + + dst_abs_fpath3 = doc_srcdir / rel_to_static_fpath + dst_abs_fpath3.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src_abs_fpath, dst_abs_fpath3) + + if INSERT_AT == 'inline': + # Try to insert after test + insert_index = info['insert_line_index'] + elif INSERT_AT == 'end': + insert_index = end_index else: - raise KeyError(mode) + raise KeyError(INSERT_AT) + lines.insert(insert_index, '.. image:: {}'.format('..' / rel_to_root_fpath)) + # lines.insert(insert_index, '.. image:: {}'.format(rel_to_root_fpath)) + # lines.insert(insert_index, '.. image:: {}'.format(rel_to_static_fpath)) + lines.insert(insert_index, '') + - lines[:] = new_lines - # make sure there is a blank line at the end - if lines and lines[-1]: - lines.append('') +def postprocess_hyperlinks(app, doctree, docname): + """ + Extension to fixup hyperlinks. + This should be connected to the Sphinx application's + "autodoc-process-docstring" event. + """ + # Your hyperlink postprocessing logic here + from docutils import nodes + import pathlib + for node in doctree.traverse(nodes.reference): + if 'refuri' in node.attributes: + refuri = node.attributes['refuri'] + if '.rst' in refuri: + if 'source' in node.document: + fpath = pathlib.Path(node.document['source']) + parent_dpath = fpath.parent + if (parent_dpath / refuri).exists(): + node.attributes['refuri'] = refuri.replace('.rst', '.html') + else: + raise AssertionError + + +def fix_rst_todo_section(lines): + new_lines = [] + for line in lines: + ... + ... def setup(app): + import sphinx + app : sphinx.application.Sphinx = app app.add_domain(PatchedPythonDomain, override=True) - if 1: - # New Way - # what = None - app.connect('autodoc-process-docstring', process) - else: - # OLD WAY - # https://stackoverflow.com/questions/26534184/can-sphinx-ignore-certain-tags-in-python-docstrings - # Register a sphinx.ext.autodoc.between listener to ignore everything - # between lines that contain the word IGNORE - # from sphinx.ext.autodoc import between - # app.connect('autodoc-process-docstring', between('^ *Ignore:$', exclude=True)) - pass + + app.connect("doctree-resolved", postprocess_hyperlinks) + + docstring_processor = GoogleStyleDocstringProcessor() + # https://stackoverflow.com/questions/26534184/can-sphinx-ignore-certain-tags-in-python-docstrings + app.connect('autodoc-process-docstring', docstring_processor.process_docstring_callback) + + def copy(src, dst): + import shutil + print(f'Copy {src} -> {dst}') + assert src.exists() + if not dst.parent.exists(): + dst.parent.mkdir() + shutil.copy(src, dst) + + ### Hack for kwcoco: TODO: figure out a way for the user to configure this. + HACK_FOR_KWCOCO = 0 + if HACK_FOR_KWCOCO: + import pathlib + doc_outdir = pathlib.Path(app.outdir) / 'auto' + doc_srcdir = pathlib.Path(app.srcdir) / 'auto' + + mod_dpath = doc_srcdir / '../../../kwcoco' + + src_fpath = (mod_dpath / 'coco_schema.json') + copy(src_fpath, doc_outdir / src_fpath.name) + copy(src_fpath, doc_srcdir / src_fpath.name) + + src_fpath = (mod_dpath / 'coco_schema_informal.rst') + copy(src_fpath, doc_outdir / src_fpath.name) + copy(src_fpath, doc_srcdir / src_fpath.name) return app diff --git a/plottool_ibeis/__init__.py b/plottool_ibeis/__init__.py old mode 100755 new mode 100644 index ea1420b..0e9e74b --- a/plottool_ibeis/__init__.py +++ b/plottool_ibeis/__init__.py @@ -2,7 +2,7 @@ """ Wrappers around matplotlib """ -__version__ = '2.2.1' +__version__ = '2.3.0' import utool as ut ut.noinject(__name__, '[plottool_ibeis.__init__]') diff --git a/plottool_ibeis/__main__.py b/plottool_ibeis/__main__.py index d3961a7..6690724 100644 --- a/plottool_ibeis/__main__.py +++ b/plottool_ibeis/__main__.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import, division, print_function def plottool_ibeis_main(): diff --git a/plottool_ibeis/_cv2_impaint.py b/plottool_ibeis/_cv2_impaint.py index d3c2238..2b08562 100644 --- a/plottool_ibeis/_cv2_impaint.py +++ b/plottool_ibeis/_cv2_impaint.py @@ -197,7 +197,7 @@ def draw_circle(event, x, y, flags, param): cv2.namedWindow('image') cv2.setMouseCallback('image', draw_circle) - while(1): + while True: cv2.imshow('image', img) keycode = cv2.waitKey(1) & 0xFF if keycode == ord('m'): diff --git a/plottool_ibeis/abstract_interaction.py b/plottool_ibeis/abstract_interaction.py old mode 100755 new mode 100644 index e149438..4ddec2e --- a/plottool_ibeis/abstract_interaction.py +++ b/plottool_ibeis/abstract_interaction.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- """ Known Interactions that use AbstractInteraction: pt.MatchInteraction2 pt.MultiImageInteraction ibeis.NameInteraction """ -from __future__ import absolute_import, division, print_function +import ubelt as ub import six import re import utool as ut @@ -198,7 +197,7 @@ def update(self): def on_scroll(self, event): if self.debug: print('[pt.a] on_scroll') - print(ut.repr3(event.__dict__)) + print(ub.urepr(event.__dict__, nl=1)) pass def close(self): diff --git a/plottool_ibeis/color_funcs.py b/plottool_ibeis/color_funcs.py old mode 100755 new mode 100644 index fc1e4d0..9df78fe --- a/plottool_ibeis/color_funcs.py +++ b/plottool_ibeis/color_funcs.py @@ -1,7 +1,5 @@ -from __future__ import absolute_import, division, print_function -from six.moves import range, zip, map # NOQA -from plottool_ibeis import custom_constants # NOQA import six +import ubelt as ub from matplotlib import colors as mcolors import colorsys import numpy as np # NOQA @@ -22,7 +20,6 @@ def _test_base01(channels): def _test_base255(channels): tests255 = { - #'is_int': all([ut.is_int(c) for c in channels]), 'is_255': all([c >= 0.0 and c <= 255.0 for c in channels]), } return tests255 @@ -224,9 +221,10 @@ def testshow_colors(rgb_list, gray=ut.get_argflag('--gray')): import vtool_ibeis as vt block = np.zeros((5, 5, 3)) block_list = [block + color[0:3] for color in rgb_list] - #print(ut.repr2(block_list)) - #print(ut.repr2(rgb_list)) - chunks = ut.ichunks(block_list, 10) + #print(ub.urepr(block_list)) + #print(ub.urepr(rgb_list)) + + chunks = ub.chunks(block_list, chunksize=10) stacked_chunk = [] for chunk in chunks: stacked_chunk.append(vt.stack_image_list(chunk, vert=False)) @@ -256,7 +254,7 @@ def desaturate_rgb(rgb, amount): >>> color_list = [rgb, new_rgb, desaturate_rgb(rgb, .7)] >>> testshow_colors(color_list) >>> # verify results - >>> result = ut.repr2(new_rgb) + >>> result = ub.urepr(new_rgb, nl=0) >>> print(result) (1.0, 0.696078431372549, 0.5) @@ -296,7 +294,7 @@ def lighten_rgb(rgb, amount): >>> color_list = [rgb, new_rgb, lighten_rgb(rgb, .5)] >>> testshow_colors(color_list) >>> # verify results - >>> result = ut.repr2(new_rgb, with_dtype=False) + >>> result = ub.urepr(new_rgb, with_dtype=False) >>> print(result) """ hue_adjust = 0.0 @@ -638,18 +636,18 @@ def show_all_colormaps(): maps.sort() else: maps = CMAP_DICT[type_] - print('CMAP_DICT = %s' % (ut.repr3(CMAP_DICT),)) + print('CMAP_DICT = %s' % (ub.urepr(CMAP_DICT),)) cmap_ = ut.get_argval('--cmap', default=None) if cmap_ is not None: maps = [getattr(plt.cm, cmap_)] - l = len(maps) + 1 + ell = len(maps) + 1 for i, m in enumerate(maps): if TRANSPOSE: - pylab.subplot(l, 1, i + 1) + pylab.subplot(ell, 1, i + 1) else: - pylab.subplot(1, l, i + 1) + pylab.subplot(1, ell, i + 1) #pylab.axis("off") ax = plt.gca() @@ -672,11 +670,7 @@ def show_all_colormaps(): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.color_funcs - python -m plottool_ibeis.color_funcs --allexamples - python -m plottool_ibeis.color_funcs --allexamples --noface --nosrc + python -m plottool_ibeis.color_funcs all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/color_funcs.pyi b/plottool_ibeis/color_funcs.pyi index 2e1c1cf..b815b4e 100644 --- a/plottool_ibeis/color_funcs.pyi +++ b/plottool_ibeis/color_funcs.pyi @@ -1,6 +1,5 @@ from typing import Any from _typeshed import Incomplete -from typing import Any def is_base01(channels): diff --git a/plottool_ibeis/custom_constants.py b/plottool_ibeis/custom_constants.py old mode 100755 new mode 100644 diff --git a/plottool_ibeis/custom_figure.py b/plottool_ibeis/custom_figure.py old mode 100755 new mode 100644 index 89af28b..2221f0b --- a/plottool_ibeis/custom_figure.py +++ b/plottool_ibeis/custom_figure.py @@ -1,5 +1,6 @@ from os.path import exists, splitext, join, split import utool as ut +import ubelt as ub import matplotlib as mpl import warnings import functools @@ -372,7 +373,7 @@ def save_figure(fnum=None, fpath=None, fpath_strict=None, usetitle=False, #print('[df2] bbox ar = %.2f' % np.abs((extent.width / extent.height,))) savekw['bbox_inches'] = extent.expanded(1.0, 1.0) if verbose == 2: - print('[pt.save_figure] savekw = ' + ut.repr2(savekw)) + print('[pt.save_figure] savekw = ' + ub.urepr(savekw)) with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=DeprecationWarning) @@ -384,7 +385,7 @@ def save_figure(fnum=None, fpath=None, fpath_strict=None, usetitle=False, print('[pt.save_figure] save_figure() ndir=%r' % (fpathndir)) if verbose > 1 or ut.VERBOSE: print(']pt.save_figure] fpath_clean = %s' % (fpath_clean, )) - print('[pt.save_figure] savekw = ' + ut.repr2(savekw)) + print('[pt.save_figure] savekw = ' + ub.urepr(savekw)) if fpath_clean.endswith('.png'): savekw['transparent'] = True savekw['edgecolor'] = 'none' diff --git a/plottool_ibeis/custom_figure.pyi b/plottool_ibeis/custom_figure.pyi index 1a6bf0c..0f37ac9 100644 --- a/plottool_ibeis/custom_figure.pyi +++ b/plottool_ibeis/custom_figure.pyi @@ -1,10 +1,7 @@ -from typing import Union from typing import Tuple import matplotlib as mpl from typing import Any -import matplotlib as mpl from _typeshed import Incomplete -from typing import Any def customize_figure(fig, docla): @@ -39,10 +36,10 @@ def get_ax(fnum: Incomplete | None = ..., pnum: Incomplete | None = ...): ... -def figure(fnum: Union[int, None] = None, - pnum: Union[int, str, Tuple[int, int, int]] = ..., +def figure(fnum: int | None = None, + pnum: int | str | Tuple[int, int, int] = ..., docla: bool = False, - title: Union[str, None] = None, + title: str | None = None, figtitle: None = None, doclf: bool = False, projection: None = None, @@ -79,16 +76,16 @@ def get_image_from_figure(fig): ... -def save_figure(fnum: Union[int, None] = None, - fpath: Union[str, None] = None, - fpath_strict: Union[str, None] = None, +def save_figure(fnum: int | None = None, + fpath: str | None = None, + fpath_strict: str | None = None, usetitle: bool = False, overwrite: bool = True, - defaultext: Union[str, None] = None, + defaultext: str | None = None, verbose: int = 1, - dpi: Union[int, None] = None, - figsize: Union[tuple[int, int], None] = None, - saveax: Union[bool, mpl.axes.Axes, None] = None, + dpi: int | None = None, + figsize: tuple[int, int] | None = None, + saveax: bool | mpl.axes.Axes | None = None, fig: Incomplete | None = ..., dpath: Incomplete | None = ...): ... diff --git a/plottool_ibeis/draw_func2.py b/plottool_ibeis/draw_func2.py old mode 100755 new mode 100644 index 1ec8588..8ec52cf --- a/plottool_ibeis/draw_func2.py +++ b/plottool_ibeis/draw_func2.py @@ -288,15 +288,16 @@ def overlay_icon(icon, coords=(0, 0), coord_type='axes', bbox_alignment=(0, 0), CommandLine: python -m plottool_ibeis.draw_func2 --exec-overlay_icon --show --icon zebra.png - python -m plottool_ibeis.draw_func2 --exec-overlay_icon --show --icon astro.png - python -m plottool_ibeis.draw_func2 --exec-overlay_icon --show --icon astro.png --artist + python -m plottool_ibeis.draw_func2 --exec-overlay_icon --show --icon astro + python -m plottool_ibeis.draw_func2 --exec-overlay_icon --show --icon astro --artist Example: >>> # DISABLE_DOCTEST >>> from plottool_ibeis.draw_func2 import * # NOQA >>> import plottool_ibeis as pt >>> pt.plot2(np.arange(100), np.arange(100)) - >>> icon = ut.get_argval('--icon', type_=str, default='astro.png') + >>> icon = ut.get_argval('--icon', type_=str, default='astro') + >>> icon = ut.grab_test_imgpath(icon) >>> coords = (0, 0) >>> coord_type = 'axes' >>> bbox_alignment = (0, 0) @@ -591,7 +592,7 @@ def extract_axes_extents(fig, combine=False, pad=0.0): groupid_list.append(groupid) groupxs = ut.group_indices(groupid_list)[1] - new_groups = ut.lmap(ut.flatten, ut.apply_grouping(atomic_axes, groupxs)) + new_groups = [list(ub.flatten(g)) for g in ut.apply_grouping(atomic_axes, groupxs)] atomic_axes = new_groups #[[(ax.rowNum, ax.colNum) for ax in axs] for axs in atomic_axes] # save all rows of each column @@ -833,7 +834,7 @@ def show_if_requested(N=1): groupid_list.append(groupid) groups = ub.group_items(atomic_axes, groupid_list) - new_groups = ut.emap(ut.flatten, groups.values()) + new_groups = [list(ub.flatten(g)) for g in groups.values()] atomic_axes = new_groups #[[(ax.rowNum, ax.colNum) for ax in axs] for axs in atomic_axes] # save all rows of each column @@ -2196,13 +2197,32 @@ def space_yticks(nTicks=9, spacing=32, ax=None): def small_xticks(ax=None): + """ + Example: + >>> # ENABLE_DOCTEST + >>> from plottool_ibeis.draw_func2 import * # NOQA + >>> import plottool_ibeis as pt + >>> fig = pt.figure() + >>> ax = fig.gca() + >>> small_xticks(ax) + """ for tick in ax.xaxis.get_major_ticks(): - tick.label.set_fontsize(8) + # Replaced for matplotlib 3.8 + # https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.8.0.html#unused-methods-in-axis-tick-xaxis-and-yaxis + try: + tick.label.set_fontsize(8) + except AttributeError: + tick.label1.set_fontsize(8) def small_yticks(ax=None): for tick in ax.yaxis.get_major_ticks(): - tick.label.set_fontsize(8) + # Replaced for matplotlib 3.8 + # https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.8.0.html#unused-methods-in-axis-tick-xaxis-and-yaxis + try: + tick.label.set_fontsize(8) + except AttributeError: + tick.label1.set_fontsize(8) def plot_bars(y_data, nColorSplits=1): @@ -2567,13 +2587,19 @@ def ensure_divider(ax): from plottool_ibeis import plot_helpers as ph divider = ph.get_plotdat(ax, DF2_DIVIDER_KEY, None) if divider is None: - divider = make_axes_locatable(ax) + divider = make_axes_locatable(ax) # type: mpl_toolkits.axes_grid1.axes_divider.AxesDivider ph.set_plotdat(ax, DF2_DIVIDER_KEY, divider) orig_append_axes = divider.append_axes def df2_append_axes(divider, position, size, pad=None, add_to_figure=True, **kwargs): """ override divider add axes to register the divided axes """ div_axes = ph.get_plotdat(ax, 'df2_div_axes', []) - new_ax = orig_append_axes(position, size, pad=pad, add_to_figure=add_to_figure, **kwargs) + # https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.7.0.html + try: + new_ax = orig_append_axes(position, size, pad=pad, add_to_figure=add_to_figure, **kwargs) + except Exception: + new_ax = orig_append_axes(position, size, pad=pad, **kwargs) + if add_to_figure: + divider._fig.add_axes(ax) div_axes.append(new_ax) ph.set_plotdat(ax, 'df2_div_axes', div_axes) return new_ax @@ -3283,7 +3309,7 @@ def draw_keypoint_patch(rchip, kp, sift=None, warped=False, patch_dict={}, **kwa >>> # DISABLE_DOCTEST >>> from plottool_ibeis.draw_func2 import * # NOQA >>> import vtool_ibeis as vt - >>> rchip = vt.imread(ut.grab_test_imgpath('astro.png')) + >>> rchip = vt.imread(ut.grab_test_imgpath('astro')) >>> kp = [100, 100, 20, 0, 20, 0] >>> sift = None >>> warped = True @@ -3366,7 +3392,7 @@ def imshow(img, fnum=None, title=None, figtitle=None, pnum=None, >>> # ENABLE_DOCTEST >>> from plottool_ibeis.draw_func2 import * # NOQA >>> import vtool_ibeis as vt - >>> img_fpath = ut.grab_test_imgpath('carl.jpg') + >>> img_fpath = ut.grab_test_imgpath('carl') >>> img = vt.imread(img_fpath) >>> (fig, ax) = imshow(img) >>> result = ('(fig, ax) = %s' % (str((fig, ax)),)) @@ -3621,8 +3647,8 @@ def show_chipmatch2(rchip1, rchip2, kpts1=None, kpts2=None, fm=None, fs=None, >>> from plottool_ibeis.draw_func2 import * # NOQA >>> import plottool_ibeis as pt >>> import vtool_ibeis as vt - >>> rchip1 = vt.imread(ut.grab_test_imgpath('easy1.png')) - >>> rchip2 = vt.imread(ut.grab_test_imgpath('easy2.png')) + >>> rchip1 = vt.imread(ut.grab_test_imgpath('tsukuba_r')) + >>> rchip2 = vt.imread(ut.grab_test_imgpath('tsukuba_l')) >>> kpts1 = np.array([ >>> [10, 10, 30, 0, 30, 0. ], >>> [ 355.89, 142.95, 10.46, -0.63, 8.59, 0. ], diff --git a/plottool_ibeis/draw_func2.pyi b/plottool_ibeis/draw_func2.pyi index d71d355..7ac854f 100644 --- a/plottool_ibeis/draw_func2.pyi +++ b/plottool_ibeis/draw_func2.pyi @@ -1,13 +1,10 @@ -from typing import Union from numpy import ndarray import matplotlib as mpl from typing import Callable from typing import Any from typing import List -import matplotlib as mpl from _typeshed import Incomplete from collections.abc import Generator -from typing import Any DEBUG: bool print: Incomplete @@ -128,7 +125,7 @@ class OffsetImage2(mpl.offsetbox.OffsetBox): ... -def overlay_icon(icon: Union[ndarray, str], +def overlay_icon(icon: ndarray | str, coords: tuple = ..., coord_type: str = 'axes', bbox_alignment: tuple = ..., @@ -451,16 +448,16 @@ def draw_stems(x_data: None = None, def plot_sift_signature( sift: ndarray, title: str = '', - fnum: Union[int, None] = None, - pnum: Union[tuple, str, int, None] = None) -> mpl.axes.AxesSubplot: + fnum: int | None = None, + pnum: tuple | str | int | None = None) -> mpl.axes.AxesSubplot: ... def plot_descriptor_signature( vec: ndarray, title: str = '', - fnum: Union[int, None] = None, - pnum: Union[tuple, None] = None) -> mpl.axes.AxesSubplot: + fnum: int | None = None, + pnum: tuple | None = None) -> mpl.axes.AxesSubplot: ... @@ -659,7 +656,7 @@ def draw_kpts2(kpts: ndarray, ell_linewidth: float = 1.5, ell_color: None = None, pts_color: ndarray = ORANGE, - color_list: Union[list, None] = None, + color_list: list | None = None, pts_alpha: float = ..., siftkw=..., H: Incomplete | None = ..., @@ -682,7 +679,7 @@ def draw_keypoint_gradient_orientations(rchip, def draw_keypoint_patch(rchip: ndarray, kp: ndarray, - sift: Union[Any, None] = None, + sift: Any | None = None, warped: bool = False, patch_dict: dict = ..., **kwargs) -> mpl.axes.Axes: @@ -690,10 +687,10 @@ def draw_keypoint_patch(rchip: ndarray, def imshow(img: ndarray, - fnum: Union[int, None] = None, - title: Union[str, None] = None, + fnum: int | None = None, + title: str | None = None, figtitle: None = None, - pnum: Union[tuple, str, int, None] = None, + pnum: tuple | str | int | None = None, interpolation: str = 'nearest', cmap: None = None, heatmap: bool = False, @@ -721,24 +718,24 @@ def draw_vector_field(gx, def show_chipmatch2(rchip1: ndarray, rchip2: ndarray, - kpts1: Union[ndarray, None] = None, - kpts2: Union[ndarray, None] = None, - fm: Union[list, None] = None, - fs: Union[list, None] = None, + kpts1: ndarray | None = None, + kpts2: ndarray | None = None, + fm: list | None = None, + fs: list | None = None, fm_norm: None = None, - title: Union[str, None] = None, + title: str | None = None, vert: None = None, - fnum: Union[int, None] = None, - pnum: Union[tuple, str, int, None] = None, + fnum: int | None = None, + pnum: tuple | str | int | None = None, heatmap: bool = False, modifysize: bool = False, new_return: bool = False, draw_fmatch: bool = True, - darken: Union[float, None] = DARKEN, - H1: Union[ndarray, None] = None, - H2: Union[ndarray, None] = None, + darken: float | None = DARKEN, + H1: ndarray | None = None, + H2: ndarray | None = None, sel_fm: list = ..., - ax: Union[mpl.axes.Axes, None] = None, + ax: mpl.axes.Axes | None = None, heatmask: bool = False, white_background: bool = ..., **kwargs) -> tuple: @@ -750,12 +747,12 @@ def plot_fmatch(xywh1: tuple, kpts1: ndarray, kpts2: ndarray, fm: list, - fs: Union[list, None] = None, + fs: list | None = None, fm_norm: None = None, lbl1: None = None, lbl2: None = None, fnum: None = None, - pnum: Union[None, tuple, str, int] = None, + pnum: None | tuple | str | int = None, rect: bool = False, colorbar_: bool = True, draw_border: bool = False, @@ -779,8 +776,8 @@ def draw_boxedX(xywh: Incomplete | None = ..., def color_orimag(gori: ndarray, - gmag: Union[ndarray, None] = None, - gmag_is_01: Union[bool, None] = None, + gmag: ndarray | None = None, + gmag_is_01: bool | None = None, encoding: str = ..., p: float = 0.5) -> ndarray: ... diff --git a/plottool_ibeis/draw_sv.py b/plottool_ibeis/draw_sv.py old mode 100755 new mode 100644 index 557d52a..58cde03 --- a/plottool_ibeis/draw_sv.py +++ b/plottool_ibeis/draw_sv.py @@ -16,7 +16,6 @@ def get_blended_chip(chip1, chip2, M): return chip2_blendM -#@ut.indent_func def show_sv(chip1, chip2, kpts1, kpts2, fm, homog_tup=None, aff_tup=None, mx=None, show_assign=True, show_lines=True, show_kpts=True, show_aff=None, fnum=1, refine_method=None, **kwargs): @@ -198,10 +197,7 @@ def show_sv_simple(chip1, chip2, kpts1, kpts2, fm, inliers, mx=None, fnum=1, ver if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.draw_sv - python -m plottool_ibeis.draw_sv --allexamples - python -m plottool_ibeis.draw_sv --allexamples --noface --nosrc + python -m plottool_ibeis.draw_sv all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/fig_presenter.py b/plottool_ibeis/fig_presenter.py old mode 100755 new mode 100644 index d0f42e4..0e042dc --- a/plottool_ibeis/fig_presenter.py +++ b/plottool_ibeis/fig_presenter.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, division, print_function import sys import time import utool as ut diff --git a/plottool_ibeis/interact_annotations.py b/plottool_ibeis/interact_annotations.py old mode 100755 new mode 100644 index cdb587f..75700a8 --- a/plottool_ibeis/interact_annotations.py +++ b/plottool_ibeis/interact_annotations.py @@ -26,9 +26,25 @@ CommandLine: python -m plottool_ibeis.interact_annotations --test-test_interact_annots --show + + +To Liberate: + + import liberator + lib = liberator.Liberator() + lib.add_dynamic(AnnotationInteraction) + # lib.expand(['vtool', 'plottool', 'utool', 'plottool_ibeis']) + lib.expand(['plottool_ibeis']) + text = lib.current_sourcecode() + + import kwplot + fpath = ub.Path(kwplot.__file__).parent / 'box_annotator.py' + fpath.write_text(text) + """ import six import re +import ubelt as ub import numpy as np try: import vtool_ibeis as vt @@ -121,7 +137,7 @@ def axes_init(poly, ax): poly.metadata_tag = ax.text( 0, 0, #tagpos[0] + 5, tagpos[1] + 80, - ut.repr3(metadata_, nobr=True), + ub.urepr(metadata_, nobr=True, nl=True), bbox={'facecolor': 'white', 'alpha': .7}, verticalalignment='top', ) @@ -238,7 +254,10 @@ def update_color(poly, selected=False, editing_parts=False): line = poly.lines line_color = line.get_color() desel_color = df2.WHITE if poly.is_orig else df2.LIGHTGRAY - if np.any(line_color != np.array(desel_color)): + try: + if np.any(line_color != np.array(desel_color)): + line.set_color(np.array(desel_color)) + except Exception: line.set_color(np.array(desel_color)) def update_lines(poly): @@ -362,7 +381,6 @@ def size(poly): return vt.bbox_from_verts(poly.xy)[2:4] -@six.add_metaclass(ut.ReloadingMetaclass) class AnnotationInteraction(abstract_interaction.AbstractInteraction): """ An interactive polygon editor. @@ -467,7 +485,6 @@ def reinitialize_figure(self, fnum=None): self.fig.clear() self.fig.clf() #self.fig.cla() - #ut.qflag() self.fnum = fnum #print(self.fnum) ax = df2.gca() @@ -493,7 +510,7 @@ def add_action_buttons(self): # self.append_button( # 'Add Full Annotation\n' + pretty_hotkey_map(ADD_RECTANGLE_FULL_HOTKEY), # rect=[0.34, 0.015, self.but_width, self.but_height], - # callback=ut.partial(self.add_new_poly, full=True) + # callback=partial(self.add_new_poly, full=True) # ) self.append_button( 'Delete Annotation\n' + pretty_hotkey_map(DEL_RECTANGLE_HOTKEY), @@ -622,14 +639,14 @@ def get_poly_under_cursor(self, x, y): poly_dict = {k: v for k, v in self.editable_polys.items() if v is not None} if len(poly_dict) > 0: poly_inds = list(poly_dict.keys()) - poly_list = ut.take(poly_dict, poly_inds) + poly_list = list(ub.take(poly_dict, poly_inds)) # Put polygon coords into figure space poly_pts = [poly.get_transform().transform(np.asarray(poly.xy)) for poly in poly_list] # Find the nearest vertex from the annotations ind_dist_list = [vt.nearest_point(x, y, polypts) for polypts in poly_pts] - dist_lists = ut.take_column(ind_dist_list, 1) + dist_lists = [r[1] for r in ind_dist_list] min_idx = np.argmin(dist_lists) sel_polyind = poly_inds[min_idx] sel_vertx, sel_dist = ind_dist_list[min_idx] @@ -897,7 +914,7 @@ def _make_options(): metadata = self._selected_poly.metadata options = [] options += [ - #('Foo: ', ut.partial(print, 'bar')), + #('Foo: ', partial(print, 'bar')), #('Move to back ', self._selected_poly.move_to_back), ('PolyInfo: ', self._selected_poly.print_info), ] @@ -1339,7 +1356,7 @@ def enforce_dims(ax, xy_pt, margin=0.5): def test_interact_annots(): r""" CommandLine: - python -m plottool_ibeis.interact_annotations --test-test_interact_annots --show + xdoctest -m plottool_ibeis.interact_annotations test_interact_annots --show Example: >>> # ENABLE_DOCTEST @@ -1360,7 +1377,7 @@ def test_interact_annots(): #if img is None: try: img_url = 'http://i.imgur.com/Vq9CLok.jpg' - img_fpath = ut.grab_file_url(img_url) + img_fpath = ub.grabdata(img_url, appname='plottool') img = vt.imread(img_fpath) except Exception as ex: print('[interact_annot] cant read zebra: %r' % ex) diff --git a/plottool_ibeis/interact_helpers.py b/plottool_ibeis/interact_helpers.py old mode 100755 new mode 100644 diff --git a/plottool_ibeis/interact_impaint.py b/plottool_ibeis/interact_impaint.py index b86b676..a8d3727 100644 --- a/plottool_ibeis/interact_impaint.py +++ b/plottool_ibeis/interact_impaint.py @@ -9,6 +9,7 @@ http://stackoverflow.com/questions/20289939/pause-execution-until-button-press """ import utool as ut +import ubelt as ub import numpy as np try: import vtool_ibeis as vt @@ -41,12 +42,12 @@ def __init__(self, img, **kwargs): self.img = img self.brush_size = 75 import plottool_ibeis as pt - self.valid_colors1 = ut.odict([ + self.valid_colors1 = ub.odict([ #('background', (255 * pt.BLACK).tolist()), ('scenery', (255 * pt.BLACK).tolist()), ('photobomb', (255 * pt.RED).tolist()), ]) - self.valid_colors2 = ut.odict([ + self.valid_colors2 = ub.odict([ ('foreground', (255 * pt.WHITE).tolist()), ]) self.color1_idx = 0 @@ -77,7 +78,10 @@ def static_plot(self, fnum=None, pnum=(1, 1, 1)): def update_image(self): import plottool_ibeis as pt #print('update_image') + + # FIXME: this is no longer available. How do we update? self.ax.images.pop() + #self.ax.imshow(self.mask, interpolation='nearest', alpha=0.6) pt.imshow(self.mask, ax=self.ax, interpolation='nearest', alpha=0.6) self.draw() @@ -126,9 +130,9 @@ def apply_stroke(self, x, y, color): def on_click_inside(self, event, ax): x = int(math.floor(event.xdata)) y = int(math.floor(event.ydata)) - if(event.button == self.LEFT_BUTTON): + if event.button == self.LEFT_BUTTON: self.apply_stroke(x, y, self.color1) - if(event.button == self.RIGHT_BUTTON): + if event.button == self.RIGHT_BUTTON: self.apply_stroke(x, y, self.color2) self.update_image() #self.draw() @@ -156,9 +160,9 @@ def on_drag_inside(self, event): #self.print_status() x = int(math.floor(event.xdata)) y = int(math.floor(event.ydata)) - if(event.button == self.LEFT_BUTTON): + if event.button == self.LEFT_BUTTON: self.apply_stroke(x, y, self.color1) - elif(event.button == self.RIGHT_BUTTON): + elif event.button == self.RIGHT_BUTTON: self.apply_stroke(x, y, self.color2) self.update_image() #self.do_blit() @@ -201,7 +205,7 @@ def draw_demo(): >>> pt.show_if_requested() """ import matplotlib.pyplot as plt - fpath = ut.grab_test_imgpath('zebra.png') + fpath = ut.grab_test_imgpath('astro') img = vt.imread(fpath) mask = impaint_mask2(img) print('mask = %r' % (mask,)) @@ -217,11 +221,7 @@ def draw_demo(): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.interact_impaint - python -m plottool_ibeis.interact_impaint --allexamples - python -m plottool_ibeis.interact_impaint --allexamples --noface --nosrc + python -m plottool_ibeis.interact_impaint all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/interact_keypoints.py b/plottool_ibeis/interact_keypoints.py old mode 100755 new mode 100644 index 9274e76..72c6486 --- a/plottool_ibeis/interact_keypoints.py +++ b/plottool_ibeis/interact_keypoints.py @@ -11,8 +11,8 @@ class KeypointInteraction(abstract_interaction.AbstractInteraction): r""" CommandLine: - python -m plottool_ibeis.interact_keypoints --exec-KeypointInteraction --show - python -m plottool_ibeis.interact_keypoints --exec-KeypointInteraction --show --fname=lena.png + xdoctest -m plottool_ibeis.interact_keypoints KeypointInteraction --show + xdoctest -m plottool_ibeis.interact_keypoints KeypointInteraction --show --fname=astro.png Example: >>> # DISABLE_DOCTEST diff --git a/plottool_ibeis/interact_matches.py b/plottool_ibeis/interact_matches.py index 699d983..f42668c 100644 --- a/plottool_ibeis/interact_matches.py +++ b/plottool_ibeis/interact_matches.py @@ -2,16 +2,13 @@ Unfinished non-ibeis dependent version of interact matches """ import utool as ut -import six +import ubelt as ub import numpy as np from plottool_ibeis import abstract_interaction BASE_CLASS = abstract_interaction.AbstractInteraction -# TODO: move to plottool_ibeis and decouple with IBEIS -# TODO: abstract interaction -@six.add_metaclass(ut.ReloadingMetaclass) class MatchInteraction2(BASE_CLASS): """ TODO: replace functional version with this class @@ -134,7 +131,7 @@ def chipmatch_view(self, fnum=None, pnum=(1, 1, 1), verbose=None, **kwargs_): show_matches_kw['H1'] = self.H1 show_matches_kw['H2'] = self.H2 if verbose: - print('show_matches_kw = %s' % (ut.repr2(show_matches_kw, truncate=True))) + print('show_matches_kw = %s' % (ub.urepr(show_matches_kw)[0:10])) #tup = show_matches(fm, fs, **show_matches_kw) ax, xywh1, xywh2 = pt.show_chipmatch2( @@ -172,7 +169,7 @@ def select_ith_match(self, mx): fsv = self.fsv fs = self.fs print('score stats:') - print(ut.repr2(ut.get_stats(fsv, axis=0), nl=1)) + print(ub.urepr(ut.get_stats(fsv, axis=0), nl=1)) print('fsv[mx] = %r' % (fsv[mx],)) print('fs[mx] = %r' % (fs[mx],)) #---------------------- @@ -341,11 +338,7 @@ def show_keypoint_gradient_orientations(ibs, rchip, kp, vec, fnum=None, if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.interact_matches - python -m plottool_ibeis.interact_matches --allexamples - python -m plottool_ibeis.interact_matches --allexamples --noface --nosrc + python ~/code/plottool_ibeis/plottool_ibeis/interact_matches.py all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/interact_multi_image.py b/plottool_ibeis/interact_multi_image.py old mode 100755 new mode 100644 index b0ca1f9..8c3543c --- a/plottool_ibeis/interact_multi_image.py +++ b/plottool_ibeis/interact_multi_image.py @@ -12,7 +12,6 @@ import vtool_ibeis as vt except ImportError: pass -#import utool import utool as ut ut.noinject(__name__, '[pt.interact_multiimage]') @@ -21,7 +20,6 @@ #BASE_CLASS = object -@ut.reloadable_class class MultiImageInteraction(BASE_CLASS): """ @@ -92,14 +90,15 @@ def dump_to_disk(self, dpath, num=None, prefix='temp_img'): import matplotlib.pyplot as plt # NOQA import numpy as np import plottool_ibeis as pt - dpath = ut.ensurepath(dpath) + import ubelt as ub + dpath = ub.ensuredir(dpath) num_zeros = np.ceil(np.log10(len(self.gpath_list))) total = len(self.gpath_list) if num is None: num = total fmtstr = prefix + '_%0' + str(num_zeros) + 'd.jpg' fig = pt.figure(fnum=self.fnum) - for index in ut.ProgIter(range(num), lbl='dumping images to disk'): + for index in ub.ProgIter(range(num), desc='dumping images to disk'): fig = pt.figure(fnum=self.fnum) fig.clf() ax = self._plot_index(index, {'fnum': self.fnum}) @@ -170,7 +169,7 @@ def show_page(self, pagenum=None): def _plot_index(self, index, _vizkw): gpath = self.gpath_list[index] - if ut.is_funclike(gpath): + if hasattr(gpath, '__call__'): showfunc = gpath # HACK # override of plot image function @@ -251,7 +250,7 @@ def on_click_inside(self, event, ax): elif self.MOUSE_BUTTONS[event.button] == 'left': #bbox_list = ph.get_plotdat(ax, 'bbox_list') gpath = self.gpath_list[index] - if ut.is_funclike(gpath): + if hasattr(gpath, '__call__'): print('gpath_isfunklike') print('gpath = %r' % (gpath,)) import plottool_ibeis as pt @@ -333,11 +332,7 @@ def on_key_press(self, event): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.interact_multi_image - python -m plottool_ibeis.interact_multi_image --allexamples - python -m plottool_ibeis.interact_multi_image --allexamples --noface --nosrc + python -m plottool_ibeis.interact_multi_image all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/interactions.py b/plottool_ibeis/interactions.py index b98a083..2f3d3c4 100644 --- a/plottool_ibeis/interactions.py +++ b/plottool_ibeis/interactions.py @@ -46,10 +46,11 @@ class ExpandableInteraction(abstract_interaction.AbstractInteraction): >>> from plottool_ibeis.interactions import * # NOQA >>> import numpy as np >>> import plottool_ibeis as pt + >>> from functools import partial >>> inter = pt.interactions.ExpandableInteraction() - >>> inter.append_plot(ut.partial(pt.plot_func, np.sin, stop=np.pi * 2)) - >>> inter.append_plot(ut.partial(pt.plot_func, np.cos, stop=np.pi * 2)) - >>> inter.append_plot(ut.partial(pt.plot_func, np.tan, stop=np.pi * 2)) + >>> inter.append_plot(partial(pt.plot_func, np.sin, stop=np.pi * 2)) + >>> inter.append_plot(partial(pt.plot_func, np.cos, stop=np.pi * 2)) + >>> inter.append_plot(partial(pt.plot_func, np.tan, stop=np.pi * 2)) >>> inter.start() >>> pt.show_if_requested() """ @@ -323,10 +324,7 @@ def pan_on_motion(self, event): if __name__ == '__main__': r""" CommandLine: - python -m plottool_ibeis.interactions - python -m plottool_ibeis.interactions --allexamples + python -m plottool_ibeis.interactions all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/interactions.pyi b/plottool_ibeis/interactions.pyi index abe9e54..213cfcd 100644 --- a/plottool_ibeis/interactions.pyi +++ b/plottool_ibeis/interactions.pyi @@ -2,8 +2,6 @@ from typing import Callable from _typeshed import Incomplete from plottool_ibeis import abstract_interaction -from typing import Union - __docstubs__: str @@ -33,9 +31,9 @@ class ExpandableInteraction(abstract_interaction.AbstractInteraction): def append_plot(self, func: Callable, - pnum: Union[tuple, None] = None, - ishow_func: Union[Callable, None] = None, - px: Union[int, None] = None) -> None: + pnum: tuple | None = None, + ishow_func: Callable | None = None, + px: int | None = None) -> None: ... def append_partial(self, func: Callable, *args, **kwargs) -> None: diff --git a/plottool_ibeis/mpl_keypoint.py b/plottool_ibeis/mpl_keypoint.py old mode 100755 new mode 100644 index cfbcf9f..23af44b --- a/plottool_ibeis/mpl_keypoint.py +++ b/plottool_ibeis/mpl_keypoint.py @@ -102,7 +102,7 @@ def draw_keypoints(ax, kpts_, scale_factor=1.0, offset=(0.0, 0.0), rotation=0.0, if scale_factor is None: scale_factor = 1.0 - #print('[mpl_keypoint.draw_keypoints] kwargs = ' + ut.repr2(kwargs)) + #print('[mpl_keypoint.draw_keypoints] kwargs = ' + ub.urepr(kwargs)) # ellipse and point properties pts_size = kwargs.get('pts_size', 2) pts_alpha = kwargs.get('pts_alpha', 1.0) @@ -176,7 +176,7 @@ def _draw_pts(ax, _xs, _ys, pts_size, pts_color, pts_alpha=None): #if pts_alpha is not None: # ptskw['alpha'] = pts_alpha if OLD_WAY: - #print(ut.repr2(ptskw)) + #print(ub.urepr(ptskw)) ax.scatter(_xs, _ys, **ptskw) # FIXME: THIS MIGHT CAUSE ISSUES: UNEXPECTED CALL #ax.autoscale(enable=False) @@ -232,7 +232,7 @@ def get_invVR_aff2Ds(kpts, H=None): >>> import vtool_ibeis as vt >>> import cv2 >>> import plottool_ibeis as pt - >>> img_fpath = ut.grab_test_imgpath(ut.get_argval('--fname', default='zebra.png')) + >>> img_fpath = ut.grab_test_imgpath(ut.get_argval('--fname', default='astro')) >>> imgBGR = vt.imread(img_fpath) >>> imgGray = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2GRAY) >>> mser = cv2.MSER_create() @@ -261,6 +261,7 @@ def get_invVR_aff2Ds(kpts, H=None): >>> # MINE IS MUCH LARGER (by factor of 2)) WHY? >>> # we start out with a unit circle not a half circle >>> pt.draw_keypoints(pt.gca(), kpts, pts=True, ori=True, eig=True, rect=True) + >>> ut.show_if_requested() """ import vtool_ibeis.keypoint as ktool #invVR_mats = ktool.get_invV_mats(kpts, with_trans=True, with_ori=True) @@ -364,11 +365,7 @@ def orientation_actors(kpts, H=None): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.mpl_keypoint - python -m plottool_ibeis.mpl_keypoint --allexamples - python -m plottool_ibeis.mpl_keypoint --allexamples --noface --nosrc + python -m plottool_ibeis.mpl_keypoint all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/mpl_keypoint.pyi b/plottool_ibeis/mpl_keypoint.pyi index dea029d..8326cd7 100644 --- a/plottool_ibeis/mpl_keypoint.pyi +++ b/plottool_ibeis/mpl_keypoint.pyi @@ -1,5 +1,4 @@ import matplotlib as mpl -import matplotlib as mpl from _typeshed import Incomplete diff --git a/plottool_ibeis/mpl_sift.py b/plottool_ibeis/mpl_sift.py old mode 100755 new mode 100644 index fda25f1..629cf3b --- a/plottool_ibeis/mpl_sift.py +++ b/plottool_ibeis/mpl_sift.py @@ -2,6 +2,7 @@ import numpy as np import matplotlib as mpl import utool as ut +import ubelt as ub from plottool_ibeis import color_funcs as color_fns ut.noinject(__name__, '[pt.mpl_sift]') @@ -212,7 +213,7 @@ def get_sift_collection(sift, aff=None, bin_color=BLACK, arm1_color=RED, ori_colors = color_fns.distinct_colors(16) arm_collections = [ mpl.collections.PatchCollection(patches) - for patches in ut.ichunks(arm_patches, 8) + for patches in ub.chunks(arm_patches, chunksize=8) ] for col, color in zip(arm_collections, ori_colors): col.set_color(color) @@ -305,11 +306,7 @@ def render_sift_on_patch(patch, sift): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.mpl_sift - python -m plottool_ibeis.mpl_sift --allexamples - python -m plottool_ibeis.mpl_sift --allexamples --noface --nosrc + python -m plottool_ibeis.mpl_sift all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/mpl_sift.pyi b/plottool_ibeis/mpl_sift.pyi index 10b4396..b6ceca3 100644 --- a/plottool_ibeis/mpl_sift.pyi +++ b/plottool_ibeis/mpl_sift.pyi @@ -1,7 +1,6 @@ from typing import Any from numpy import ndarray from _typeshed import Incomplete -from typing import Any TAU: Incomplete BLACK: Incomplete diff --git a/plottool_ibeis/nx_helpers.py b/plottool_ibeis/nx_helpers.py index 6fb8d72..b6c89a0 100644 --- a/plottool_ibeis/nx_helpers.py +++ b/plottool_ibeis/nx_helpers.py @@ -19,7 +19,6 @@ --install-option="--library-path=/usr/lib/graphviz/" python -c "import pygraphviz; print(pygraphviz.__file__)" python3 -c "import pygraphviz; print(pygraphviz.__file__)" - """ try: import dtool as dt @@ -27,7 +26,9 @@ pass import numpy as np import utool as ut +import ubelt as ub from functools import reduce +from collections import defaultdict (print, rrr, profile) = ut.inject2(__name__) __docstubs__ = """ @@ -99,8 +100,8 @@ def show_nx(graph, with_labels=True, fnum=None, pnum=None, layout='agraph', >>> graph.add_nodes_from(['a', 'b', 'c', 'd']) >>> graph.add_edges_from({'a': 'b', 'b': 'c', 'b': 'd', 'c': 'd'}.items()) >>> nx.set_node_attributes(graph, name='shape', values='rect') - >>> nx.set_node_attributes(graph, name='image', values={'a': ut.grab_test_imgpath('carl.jpg')}) - >>> nx.set_node_attributes(graph, name='image', values={'d': ut.grab_test_imgpath('astro.png')}) + >>> nx.set_node_attributes(graph, name='image', values={'a': ut.grab_test_imgpath('carl')}) + >>> nx.set_node_attributes(graph, name='image', values={'d': ut.grab_test_imgpath('astro')}) >>> #nx.set_node_attributes(graph, name='height', values=100) >>> with_labels = True >>> fnum = None @@ -151,9 +152,9 @@ def show_nx(graph, with_labels=True, fnum=None, pnum=None, layout='agraph', node_size = layout_info['node'].get('size') node_pos = layout_info['node'].get('pos') if node_size is not None: - size_arr = np.array(ut.take(node_size, graph.nodes())) + size_arr = np.array(list(ub.take(node_size, graph.nodes()))) half_size_arr = size_arr / 2. - pos_arr = np.array(ut.take(node_pos, graph.nodes())) + pos_arr = np.array(list(ub.take(node_pos, graph.nodes()))) # autoscale does not seem to work #ul_pos = pos_arr - half_size_arr #br_pos = pos_arr + half_size_arr @@ -773,9 +774,9 @@ def nx_agraph_layout(orig_graph, inplace=False, verbose=None, >>> data1 = dict(graph1.nodes(data=True)) >>> data2 = dict(graph4.nodes(data=True)) >>> dataP = dict(pinned_graph.nodes(data=True)) - >>> print('data1 = {}'.format(ub.repr2(data1, nl=1))) - >>> print('data2 = {}'.format(ub.repr2(data2, nl=1))) - >>> print('dataP = {}'.format(ub.repr2(dataP, nl=1))) + >>> print('data1 = {}'.format(ub.urepr(data1, nl=1))) + >>> print('data2 = {}'.format(ub.urepr(data2, nl=1))) + >>> print('dataP = {}'.format(ub.urepr(dataP, nl=1))) >>> g1pos = nx.get_node_attributes(graph1, 'pos')['1'] >>> g4pos = nx.get_node_attributes(graph4, 'pos')['1'] >>> g2pos = nx.get_node_attributes(graph2, 'pos')['1'] @@ -900,8 +901,8 @@ def nx_agraph_layout(orig_graph, inplace=False, verbose=None, print('AFTER LAYOUT\n' + str(agraph)) # TODO: just replace with a single dict of attributes - node_layout_attrs = ut.ddict(dict) - edge_layout_attrs = ut.ddict(dict) + node_layout_attrs = defaultdict(dict) + edge_layout_attrs = defaultdict(dict) #for node in agraph.nodes(): for node in graph_.nodes(): @@ -1160,10 +1161,10 @@ def draw_network2(graph, layout_info, ax, as_directed=None, hacknoedge=False, # Draw nodes large_graph = len(graph) > LARGE_GRAPH - #for edge, pts in ut.ProgIter(edge_pos.items(), length=len(edge_pos), enabled=large_graph, lbl='drawing edges'): + #for edge, pts in ub.ProgIter(edge_pos.items(), total=len(edge_pos), enabled=large_graph, desc='drawing edges'): - for node, nattrs in ut.ProgIter(graph.nodes(data=True), length=len(graph), - lbl='drawing nodes', enabled=large_graph): + for node, nattrs in ub.ProgIter(graph.nodes(data=True), total=len(graph), + desc='drawing nodes', enabled=large_graph): # shape = nattrs.get('shape', 'circle') if nattrs is None: nattrs = {} @@ -1325,8 +1326,8 @@ def get_default_edge_data(graph, edge): edge_pos = layout_info['edge'].get('ctrl_pts', None) n_invis_edge = 0 if edge_pos is not None: - for edge, pts in ut.ProgIter(edge_pos.items(), length=len(edge_pos), - enabled=large_graph, lbl='drawing edges'): + for edge, pts in ub.ProgIter(edge_pos.items(), total=len(edge_pos), + enabled=large_graph, desc='drawing edges'): data = get_default_edge_data(graph, edge) if data.get('style', None) == 'invis': @@ -1403,8 +1404,8 @@ def get_default_edge_data(graph, edge): import vtool_ibeis as vt # Compute arrow width using estimated graph size if node_size is not None and node_pos is not None: - xys = np.array(ut.take(node_pos, node_pos.keys())).T - whs = np.array(ut.take(node_size, node_pos.keys())).T + xys = np.array(list(ub.take(node_pos, node_pos.keys()))).T + whs = np.array(list(ub.take(node_size, node_pos.keys()))).T bboxes = vt.bbox_from_xywh(xys, whs, [.5, .5]) extents = vt.extent_from_bbox(bboxes) tl_pts = np.array([extents[0], extents[2]]).T @@ -1651,12 +1652,9 @@ def get_default_edge_data(graph, edge): if __name__ == '__main__': - r""" + """ CommandLine: - python -m plottool_ibeis.nx_helpers - python -m plottool_ibeis.nx_helpers --allexamples + python -m plottool_ibeis.nx_helpers all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/nx_helpers.pyi b/plottool_ibeis/nx_helpers.pyi index 799f134..bf931fe 100644 --- a/plottool_ibeis/nx_helpers.pyi +++ b/plottool_ibeis/nx_helpers.pyi @@ -2,8 +2,6 @@ import networkx import dtool as dt from _typeshed import Incomplete -from typing import Union - print: Incomplete rrr: Incomplete profile: Incomplete @@ -21,15 +19,15 @@ def ensure_nonhex_color(orig_color): def show_nx(graph: networkx.Graph, with_labels: bool = True, - fnum: Union[int, None] = None, - pnum: Union[tuple, None] = None, + fnum: int | None = None, + pnum: tuple | None = None, layout: str = 'agraph', ax: None = None, pos: None = None, - img_dict: Union[dict, None] = None, - title: Union[str, None] = None, + img_dict: dict | None = None, + title: str | None = None, layoutkw: None = None, - verbose: Union[bool, None] = None, + verbose: bool | None = None, **kwargs): ... @@ -83,7 +81,7 @@ def nx_agraph_layout(orig_graph, inplace: bool = ..., verbose: Incomplete | None = ..., return_agraph: bool = ..., - groupby: Union[str, None] = None, + groupby: str | None = None, **layoutkw): ... diff --git a/plottool_ibeis/other.py b/plottool_ibeis/other.py old mode 100755 new mode 100644 index 5706910..5daaebb --- a/plottool_ibeis/other.py +++ b/plottool_ibeis/other.py @@ -1,5 +1,4 @@ # I'm not quite sure how to organize these functions yet -from __future__ import absolute_import, division, print_function import numpy as np import vtool_ibeis.histogram as htool import utool as ut diff --git a/plottool_ibeis/plot_helpers.py b/plottool_ibeis/plot_helpers.py old mode 100755 new mode 100644 diff --git a/plottool_ibeis/plot_helpers.pyi b/plottool_ibeis/plot_helpers.pyi index e9d2b8f..abab525 100644 --- a/plottool_ibeis/plot_helpers.pyi +++ b/plottool_ibeis/plot_helpers.pyi @@ -1,6 +1,5 @@ from typing import Any from _typeshed import Incomplete -from typing import Any SIFT_OR_VECFIELD: Incomplete diff --git a/plottool_ibeis/plots.py b/plottool_ibeis/plots.py old mode 100755 new mode 100644 index bcca2c8..99237b4 --- a/plottool_ibeis/plots.py +++ b/plottool_ibeis/plots.py @@ -7,6 +7,7 @@ import matplotlib as mpl import utool as ut # NOQA import numpy as np +from functools import partial print, rrr, profile = ut.inject2(__name__) @@ -98,7 +99,7 @@ def multi_plot(xdata=None, ydata_list=[], **kwargs): else: ykeys = list(ydata_list.keys()) # Normalize input - ydata_list = ut.take(ydata_list, ykeys) + ydata_list = list(ub.take(ydata_list, ykeys)) kwargs['label_list'] = kwargs.get('label_list', ykeys) def is_listlike(data): @@ -572,7 +573,7 @@ def demo_fonts(): import matplotlib.font_manager avail_fonts = matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf') names = [matplotlib.font_manager.FontProperties(fname=fname).get_name() for fname in avail_fonts] - print('avail_fonts = %s' % ut.repr4(sorted(set(names)))) + print('avail_fonts = %s' % ub.urepr(sorted(set(names)))) xdata = [1, 2, 3, 4, 5] ydata_list = [[1, 2, 3, 4, 5], [3, 3, 3, 3, 3], [5, 4, np.nan, 2, 1], [4, 3, np.nan, 1, 0]] @@ -965,7 +966,7 @@ def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs): >>> xmin = 1 >>> xmax = top >>> (c1, c2, bbox_patch1, bbox_patch2, p) = zoom_effect01(ax1, ax2, xmin, xmax) - >>> result = ('(c1, c2, bbox_patch1, bbox_patch2, p) = %s' % (ut.repr2((c1, c2, bbox_patch1, bbox_patch2, p)),)) + >>> result = ('(c1, c2, bbox_patch1, bbox_patch2, p) = %s' % (ub.urepr((c1, c2, bbox_patch1, bbox_patch2, p)),)) >>> print(result) >>> ut.quit_if_noshow() >>> import plottool_ibeis as pt @@ -1301,8 +1302,6 @@ def make_bins2(width, start, end): except Exception as ex: ut.printex(ex, 'probably gave negative scores', keys=[ 'bins', 'data', 'total_min']) - import utool - utool.embed() raise if ut.SUPER_STRICT: @@ -1340,7 +1339,7 @@ def make_bins2(width, start, end): # ax.set_yscale('symlog', nonposy='clip') # ax.set_xscale('log', nonposx='clip') # ax.set_yscale('log', nonposy='clip') - # set_logyscale_from_data(sorted(ut.flatten(scores_list))) + # set_logyscale_from_data(sorted(ub.flatten(scores_list))) if overlay_score_domain is not None: ax = df2.gca() @@ -2079,8 +2078,8 @@ def draw_timedelta_pie(timedeltas, bins=None, fnum=None, pnum=(1, 1, 1), label=' mask = freq > 0 masked_freq = freq.compress(mask, axis=0) size = masked_freq.sum() - masked_lbls = ut.compress(bin_labels, mask) - masked_colors = ut.compress(colors, mask) + masked_lbls = list(ub.compress(bin_labels, mask)) + masked_colors = list(ub.compress(colors, mask)) explode = [0] * len(masked_freq) masked_percent = (masked_freq * 100 / size) plt.pie(masked_percent, explode=explode, autopct='%1.1f%%', @@ -2115,11 +2114,11 @@ def word_histogram2(text_list, weight_list=None, **kwargs): """ import matplotlib.pyplot as plt import plottool_ibeis as pt - text_hist = ut.dict_hist(text_list, weight_list=weight_list) + text_hist = ub.dict_hist(text_list, weights=weight_list) text_vals = list(text_hist.values()) sortx = ut.list_argsort(text_vals)[::-1] - bin_labels = ut.take(list(text_hist.keys()), sortx) - freq = np.array(ut.take(text_vals, sortx)) + bin_labels = list(ub.take(list(text_hist.keys()), sortx)) + freq = np.array(list(ub.take(text_vals, sortx))) xints = np.arange(len(bin_labels)) width = .95 @@ -2260,8 +2259,8 @@ def draw_time_distribution(unixtime_list, bw=None): # 'bandwidth': np.linspace(bw_low, bw_high, 30) 'bandwidth': np.linspace(day, day * 14, 14) } - # searcher = ut.partial(GridSearchCV, n_jobs=7) - searcher = ut.partial(RandomizedSearchCV, n_iter=5, n_jobs=8) + # searcher = partial(GridSearchCV, n_jobs=7) + searcher = partial(RandomizedSearchCV, n_iter=5, n_jobs=8) print('Searching for best bandwidth') grid = searcher(KernelDensity(kernel='gaussian'), grid_params, cv=2, verbose=0) @@ -2403,11 +2402,7 @@ def wordcloud(text, size=None, fnum=None, pnum=None, ax=None): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.plots - python -m plottool_ibeis.plots --allexamples - python -m plottool_ibeis.plots --allexamples --noface --nosrc + python -m plottool_ibeis.plots all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/plots.pyi b/plottool_ibeis/plots.pyi index 25a35e5..11c2fb5 100644 --- a/plottool_ibeis/plots.pyi +++ b/plottool_ibeis/plots.pyi @@ -2,12 +2,7 @@ from numpy import ndarray from typing import List from typing import Any import matplotlib as mpl -from typing import Union -import matplotlib as mpl from _typeshed import Incomplete -from typing import Any - -from typing import Union print: Incomplete rrr: Incomplete @@ -19,7 +14,7 @@ def is_default_dark_bg(): ... -def multi_plot(xdata: Union[ndarray, None] = None, +def multi_plot(xdata: ndarray | None = None, ydata_list: List[ndarray] = ..., **kwargs): ... @@ -57,7 +52,7 @@ def plot_rank_cumhist(cdf_list, def draw_hist_subbin_maxima(hist: ndarray, - centers: Union[Any, None] = None, + centers: Any | None = None, bin_colors: Incomplete | None = ..., maxima_thresh: Incomplete | None = ..., remove_endpoints: bool = ..., @@ -66,7 +61,7 @@ def draw_hist_subbin_maxima(hist: ndarray, def draw_subextrema(ydata: ndarray, - xdata: Union[Any, None] = None, + xdata: Any | None = None, op: str = ..., bin_colors: Incomplete | None = ..., thresh_factor: Incomplete | None = ..., @@ -117,14 +112,14 @@ def plot_score_histograms(scores_list, def plot_probabilities(prob_list: list, - prob_lbls: Union[Any, None] = None, - prob_colors: Union[Any, None] = None, - xdata: Union[Any, None] = None, - prob_thresh: Union[Any, None] = None, + prob_lbls: Any | None = None, + prob_colors: Any | None = None, + xdata: Any | None = None, + prob_thresh: Any | None = None, score_thresh: Incomplete | None = ..., figtitle: str = 'plot_probabilities', - fnum: Union[int, None] = None, - pnum: Union[tuple, str, None] = ..., + fnum: int | None = None, + pnum: tuple | str | None = ..., fill: bool = False, **kwargs) -> None: ... @@ -135,14 +130,14 @@ plot_densities = plot_probabilities def plot_sorted_scores(scores_list: list, - score_lbls: Union[Any, None] = None, - score_markers: Union[Any, None] = None, - score_colors: Union[Any, None] = None, - markersizes: Union[Any, None] = None, - fnum: Union[int, None] = None, + score_lbls: Any | None = None, + score_markers: Any | None = None, + score_colors: Any | None = None, + markersizes: Any | None = None, + fnum: int | None = None, pnum: tuple = ..., logscale: bool = True, - figtitle: Union[str, None] = None, + figtitle: str | None = None, score_label: str = ..., thresh: Incomplete | None = ..., use_stems: Incomplete | None = ..., @@ -172,7 +167,7 @@ def estimate_pdf(data, bw_factor): def interval_stats_plot(param2_stat_dict, - fnum: Union[int, None] = None, + fnum: int | None = None, pnum: tuple = ..., x_label: str = ..., y_label: str = ..., @@ -194,7 +189,7 @@ def plot_search_surface(known_nd_data: Any, known_target_points: Any, nd_labels: Any, target_label: Any, - fnum: Union[int, None] = None, + fnum: int | None = None, pnum: Incomplete | None = ..., title: Incomplete | None = ...) -> mpl.axes.Axes: ... @@ -232,9 +227,9 @@ def draw_time_distribution(unixtime_list, bw: Incomplete | None = ...) -> None: ... -def wordcloud(text: Union[str, dict], +def wordcloud(text: str | dict, size: Incomplete | None = ..., - fnum: Union[int, None] = None, - pnum: Union[tuple, None] = None, + fnum: int | None = None, + pnum: tuple | None = None, ax: Incomplete | None = ...) -> None: ... diff --git a/plottool_ibeis/py.typed b/plottool_ibeis/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/plottool_ibeis/screeninfo.py b/plottool_ibeis/screeninfo.py old mode 100755 new mode 100644 index a16b7a9..6265c02 --- a/plottool_ibeis/screeninfo.py +++ b/plottool_ibeis/screeninfo.py @@ -1,7 +1,9 @@ import six import sys import utool as ut +import ubelt as ub import numpy as np +from collections import OrderedDict try: import guitool_ibeis as gt from guitool_ibeis.__PYQT__ import QtWidgets @@ -86,7 +88,7 @@ def infer_monitor_specs(res_w, res_h, inches_diag): query_vars = [inches_w, inches_h] for solution in sympy.solve(equations, query_vars): print('Solution:') - reprstr = ut.repr3(ut.odict(zip(query_vars, solution)), explicit=True, nobr=1, with_comma=False) + reprstr = ub.urepr(OrderedDict(zip(query_vars, solution)), explicit=True, nobr=1, with_comma=False, nl=True) print(ut.indent(ut.autopep8_format(reprstr))) #(inches_diag*res_w/sqrt(res_h**2 + res_w**2), inches_diag*res_h/sqrt(res_h**2 + res_w**2)) @@ -110,7 +112,7 @@ def get_resolution_info(monitor_num=0): >>> monitor_num = 1 >>> for monitor_num in range(get_number_of_monitors()): >>> info = get_resolution_info(monitor_num) - >>> print('monitor(%d).info = %s' % (monitor_num, ut.repr3(info, precision=3))) + >>> print('monitor(%d).info = %s' % (monitor_num, ub.urepr(info, nl=True, precision=3))) """ import guitool_ibeis as gt app = gt.ensure_qtapp()[0] # NOQA @@ -208,7 +210,7 @@ def get_resolution_info(monitor_num=0): ratio = min(mm_w, mm_h) / max(mm_w, mm_h) #pixel_density = dpi_x / ppi_x - info = ut.odict([ + info = OrderedDict([ ('monitor_num', monitor_num), ('off_x', off_x), ('off_y', off_y), @@ -256,7 +258,7 @@ def get_monitor_geom(monitor_num=0): >>> from plottool_ibeis.screeninfo import * # NOQA >>> monitor_num = 0 >>> geom = get_monitor_geom(monitor_num) - >>> result = ('geom = %s' % (ut.repr2(geom),)) + >>> result = ('geom = %s' % (ub.urepr(geom),)) >>> print(result) """ gt.ensure_qtapp() @@ -366,10 +368,7 @@ def get_position_ix(ix): if __name__ == '__main__': r""" CommandLine: - python -m plottool_ibeis.screeninfo - python -m plottool_ibeis.screeninfo --allexamples + python -m plottool_ibeis.screeninfo all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/test_colorsys.py b/plottool_ibeis/test_colorsys.py old mode 100755 new mode 100644 diff --git a/plottool_ibeis/test_vtk_poly.py b/plottool_ibeis/test_vtk_poly.py index 1cc2f0f..fc34c6e 100644 --- a/plottool_ibeis/test_vtk_poly.py +++ b/plottool_ibeis/test_vtk_poly.py @@ -16,9 +16,11 @@ def rhombicuboctahedron(): # left view faces us import utool as ut + import ubelt as ub import six import itertools - counter = ut.partial(six.next, itertools.count(0)) + from functools import partial + counter = partial(six.next, itertools.count(0)) vertex_locations = vtk.vtkPoints() vertex_locations.SetNumberOfPoints(24) @@ -44,7 +46,7 @@ def rhombicuboctahedron(): print('perms = %r' % (perms,)) for x in range(3): vp = vplist[x] - p = np.vstack(ut.take(plist, perms[x])).T + p = np.vstack(list(ub.take(plist, perms[x]))).T counts = [counter() for z in range(4)] vpdict[vp] = counts vertex_array.extend(p.tolist()) @@ -59,7 +61,7 @@ def rhombicuboctahedron(): # right, down, front print('perms = %r' % (perms,)) for x in range(3): - p = np.vstack(ut.take(plist, perms[x])).T + p = np.vstack(list(ub.take(plist, perms[x]))).T counts = [counter() for z in range(4)] vp = vplist[x + 3] vpdict[vp] = counts @@ -150,11 +152,9 @@ def rhombicuboctahedron(): if 1: # Read the image data from a file - import utool as ut - textureCoords = vtk.vtkFloatArray() textureCoords.SetNumberOfComponents(3) - #coords = ut.take(vertex_array, face_dict['L']) + #coords = list(ub.take(vertex_array, face_dict['L'])) #for coord in coords: # textureCoords.InsertNextTuple(tuple(coord)) textureCoords.InsertNextTuple((0, 0, 0)) diff --git a/plottool_ibeis/tests/test_helpers.py b/plottool_ibeis/tests/test_helpers.py index b933ac0..8491c8d 100755 --- a/plottool_ibeis/tests/test_helpers.py +++ b/plottool_ibeis/tests/test_helpers.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, division, print_function def dummy_bbox(img, shiftxy=(0.0, 0.0), scale=.25): diff --git a/plottool_ibeis/tests/test_interact_multi_image.py b/plottool_ibeis/tests/test_interact_multi_image.py index c0969dd..4440c03 100755 --- a/plottool_ibeis/tests/test_interact_multi_image.py +++ b/plottool_ibeis/tests/test_interact_multi_image.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # DUPLICATE CODE, DELETE -from __future__ import absolute_import, division, print_function from plottool_ibeis import interact_multi_image from plottool_ibeis import draw_func2 as df2 import utool diff --git a/plottool_ibeis/tests/test_viz_image2.py b/plottool_ibeis/tests/test_viz_image2.py index 413bf1b..2f969b0 100755 --- a/plottool_ibeis/tests/test_viz_image2.py +++ b/plottool_ibeis/tests/test_viz_image2.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from __future__ import absolute_import, division, print_function from plottool_ibeis import viz_image2 from plottool_ibeis import draw_func2 as df2 import cv2 diff --git a/plottool_ibeis/tests/test_viz_images.py b/plottool_ibeis/tests/test_viz_images.py index d4e4693..07d5de1 100755 --- a/plottool_ibeis/tests/test_viz_images.py +++ b/plottool_ibeis/tests/test_viz_images.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from __future__ import absolute_import, division, print_function from plottool_ibeis import viz_image2 from plottool_ibeis import draw_func2 as df2 from plottool_ibeis import plot_helpers as ph diff --git a/plottool_ibeis/viz_featrow.py b/plottool_ibeis/viz_featrow.py old mode 100755 new mode 100644 index ea03714..0efd086 --- a/plottool_ibeis/viz_featrow.py +++ b/plottool_ibeis/viz_featrow.py @@ -194,11 +194,7 @@ def _draw_patch(**kwargs): if __name__ == '__main__': """ CommandLine: - python -m plottool_ibeis.viz_featrow - python -m plottool_ibeis.viz_featrow --allexamples - python -m plottool_ibeis.viz_featrow --allexamples --noface --nosrc + python -m plottool_ibeis.viz_featrow all """ - import multiprocessing - multiprocessing.freeze_support() # for win32 - import utool as ut # NOQA - ut.doctest_funcs() + import xdoctest + xdoctest.doctest_module(__file__) diff --git a/plottool_ibeis/viz_image2.py b/plottool_ibeis/viz_image2.py old mode 100755 new mode 100644 diff --git a/plottool_ibeis/viz_keypoints.py b/plottool_ibeis/viz_keypoints.py old mode 100755 new mode 100644 index 190b6ea..24748aa --- a/plottool_ibeis/viz_keypoints.py +++ b/plottool_ibeis/viz_keypoints.py @@ -2,7 +2,6 @@ import plottool_ibeis.draw_func2 as df2 import numpy as np from plottool_ibeis import plot_helpers as ph -#(print, print_, printDBG, rrr, profile) = utool.inject(__name__, '[viz_keypoints]', DEBUG=False) utool.noinject(__name__, '[viz_keypoints]') @@ -41,6 +40,8 @@ def show_keypoints(chip, kpts, fnum=0, pnum=None, **kwargs): >>> pnum = None >>> result = show_keypoints(chip, kpts, fnum, pnum) >>> print(result) + >>> import utool as ut + >>> ut.show_if_requested() """ #printDBG('[df2.show_kpts] %r' % (kwargs.keys(),)) fig, ax = df2.imshow(chip, fnum=fnum, pnum=pnum, **kwargs) @@ -51,7 +52,6 @@ def show_keypoints(chip, kpts, fnum=0, pnum=None, **kwargs): ph.draw() -#@utool.indent_func def _annotate_kpts(kpts_, sel_fx=None, **kwargs): r""" Args: @@ -62,11 +62,14 @@ def _annotate_kpts(kpts_, sel_fx=None, **kwargs): color: 3/4-tuple, ndarray, or str Example: - >>> from plottool_ibeis.viz_keypoints import * # NOQA + >>> from plottool_ibeis.viz_keypoints import _annotate_kpts >>> sel_fx = None >>> kpts = np.array([[ 92.9246, 17.5453, 7.8103, -3.4594, 10.8566, 0. ], ... [ 76.8585, 24.7918, 11.4412, -3.2634, 9.6287, 0. ], ... [ 140.6303, 24.9027, 10.4051, -10.9452, 10.5991, 0. ],]) + >>> _annotate_kpts(kpts) + >>> import utool as ut + >>> ut.show_if_requested() """ if len(kpts_) == 0: @@ -74,15 +77,16 @@ def _annotate_kpts(kpts_, sel_fx=None, **kwargs): return #color = kwargs.get('color', 'distinct' if sel_fx is None else df2.ORANGE) color = kwargs.get('color', 'scale' if sel_fx is None else df2.ORANGE) - if color == 'distinct': - # hack for distinct colors - color = df2.distinct_colors(len(kpts_)) # , randomize=True) - elif color == 'scale': - # hack for distinct colors - import vtool_ibeis as vt - #color = df2.scores_to_color(vt.get_scales(kpts_), cmap_='inferno', score_range=(0, 50)) - color = df2.scores_to_color(vt.get_scales(kpts_), cmap_='viridis', score_range=(5, 30), cmap_range=None) - #df2.distinct_colors(len(kpts_)) # , randomize=True) + if isinstance(color, str): + if color == 'distinct': + # hack for distinct colors + color = df2.distinct_colors(len(kpts_)) # , randomize=True) + elif color == 'scale': + # hack for distinct colors + import vtool_ibeis as vt + #color = df2.scores_to_color(vt.get_scales(kpts_), cmap_='inferno', score_range=(0, 50)) + color = df2.scores_to_color(vt.get_scales(kpts_), cmap_='viridis', score_range=(5, 30), cmap_range=None) + #df2.distinct_colors(len(kpts_)) # , randomize=True) # Keypoint drawing kwargs drawkpts_kw = { 'ell': True, @@ -101,8 +105,8 @@ def _annotate_kpts(kpts_, sel_fx=None, **kwargs): nonsel_kpts_ = np.vstack((kpts_[0:sel_fx], kpts_[sel_fx + 1:])) # Draw selected keypoint sel_kpts = kpts_[sel_fx:sel_fx + 1] - import utool as ut - if ut.isiterable(color) and ut.isiterable(color[0]): + import ubelt as ub + if ub.iterable(color) and ub.iterable(color[0]): # hack for distinct colors drawkpts_kw['ell_color'] = color[0:sel_fx] + color[sel_fx + 1:] drawkpts_kw diff --git a/plottool_ibeis/viz_keypoints.pyi b/plottool_ibeis/viz_keypoints.pyi index 3de8933..e6111c4 100644 --- a/plottool_ibeis/viz_keypoints.pyi +++ b/plottool_ibeis/viz_keypoints.pyi @@ -1,6 +1,4 @@ from numpy import ndarray -from typing import Union -from typing import Any from typing import Any @@ -11,6 +9,6 @@ def testdata_kpts(): def show_keypoints(chip: ndarray, kpts: ndarray, fnum: int = 0, - pnum: Union[tuple, None, Any] = None, + pnum: tuple | None | Any = None, **kwargs) -> None: ... diff --git a/publish.sh b/publish.sh index cee2385..237ee0d 100755 --- a/publish.sh +++ b/publish.sh @@ -1,6 +1,6 @@ -#!/bin/bash -__doc__=''' -Script to publish a new version of this library on PyPI. +#!/usr/bin/env bash +__doc__=' +Script to publish a new version of this library on PyPI. If your script has binary dependencies then we assume that you have built a proper binary wheel with auditwheel and it exists in the wheelhouse directory. @@ -12,35 +12,39 @@ signing, but nothing will be uploaded to pypi unless the user explicitly sets DO_UPLOAD=True or answers yes to the prompts. Args: - TWINE_USERNAME (str) : + TWINE_USERNAME (str) : username for pypi. This must be set if uploading to pypi. Defaults to "". - TWINE_PASSWORD (str) : + TWINE_PASSWORD (str) : password for pypi. This must be set if uploading to pypi. Defaults to "". - DO_GPG (bool) : + DO_GPG (bool) : If True, sign the packages with a GPG key specified by `GPG_KEYID`. defaults to auto. - DO_UPLOAD (bool) : + DO_OTS (bool) : + If True, make an opentimestamp for the package and signature (if + available) + + DO_UPLOAD (bool) : If True, upload the packages to the pypi server specified by `TWINE_REPOSITORY_URL`. - DO_BUILD (bool) : + DO_BUILD (bool) : If True, will execute the setup.py build script, which is expected to use setuptools. In the future we may add support for other build systems. If False, this script will expect the pre-built packages to exist in "wheelhouse/{NAME}-{VERSION}-{SUFFIX}.{EXT}". - Defaults to "auto". + Defaults to "auto". - DO_TAG (bool) : - if True, will "git tag" the current HEAD with + DO_TAG (bool) : + if True, will "git tag" the current HEAD with - TWINE_REPOSITORY_URL (url) : - The URL of the pypi server to upload to. + TWINE_REPOSITORY_URL (url) : + The URL of the pypi server to upload to. Defaults to "auto", which if on the release branch, this will default to the live pypi server `https://upload.pypi.org/legacy` otherwise this will default to the test.pypi server: @@ -50,11 +54,11 @@ Args: The keyid of the gpg key to sign with. (if DO_GPG=True). Defaults to the local git config user.signingkey - DEPLOY_REMOTE (str) : + DEPLOY_REMOTE (str) : The git remote to push any tags to. Defaults to "origin" - GPG_EXECUTABLE (path) : - Path to the GPG executable. + GPG_EXECUTABLE (path) : + Path to the GPG executable. Defaults to "auto", which chooses "gpg2" if it exists, otherwise "gpg". MODE (str): @@ -84,8 +88,8 @@ Usage: # Set your variables or load your secrets export TWINE_USERNAME= export TWINE_PASSWORD= - TWINE_REPOSITORY_URL="https://test.pypi.org/legacy/" -''' + TWINE_REPOSITORY_URL="https://test.pypi.org/legacy/" +' DEBUG=${DEBUG:=''} if [[ "${DEBUG}" != "" ]]; then @@ -111,9 +115,9 @@ check_variable(){ normalize_boolean(){ ARG=$1 ARG=$(echo "$ARG" | awk '{print tolower($0)}') - if [ "$ARG" = "true" ] || [ "$ARG" = "1" ] || [ "$ARG" = "yes" ] || [ "$ARG" = "on" ]; then + if [ "$ARG" = "true" ] || [ "$ARG" = "1" ] || [ "$ARG" = "yes" ] || [ "$ARG" = "y" ] || [ "$ARG" = "on" ]; then echo "True" - elif [ "$ARG" = "false" ] || [ "$ARG" = "0" ] || [ "$ARG" = "no" ] || [ "$ARG" = "off" ]; then + elif [ "$ARG" = "false" ] || [ "$ARG" = "0" ] || [ "$ARG" = "no" ] || [ "$ARG" = "n" ] || [ "$ARG" = "off" ]; then echo "False" else echo "$ARG" @@ -138,11 +142,21 @@ DO_UPLOAD=${DO_UPLOAD:=$ARG_1} DO_TAG=${DO_TAG:=$ARG_1} DO_GPG=${DO_GPG:="auto"} -# Verify that we want to build if [ "$DO_GPG" == "auto" ]; then DO_GPG="True" fi +DO_OTS=${DO_OTS:="auto"} +if [ "$DO_OTS" == "auto" ]; then + # Do opentimestamp if it is available + # python -m pip install opentimestamps-client + if type ots ; then + DO_OTS="True" + else + DO_OTS="False" + fi +fi + DO_BUILD=${DO_BUILD:="auto"} # Verify that we want to build if [ "$DO_BUILD" == "auto" ]; then @@ -150,6 +164,7 @@ if [ "$DO_BUILD" == "auto" ]; then fi DO_GPG=$(normalize_boolean "$DO_GPG") +DO_OTS=$(normalize_boolean "$DO_OTS") DO_BUILD=$(normalize_boolean "$DO_BUILD") DO_UPLOAD=$(normalize_boolean "$DO_UPLOAD") DO_TAG=$(normalize_boolean "$DO_TAG") @@ -162,7 +177,7 @@ DEFAULT_LIVE_TWINE_REPO_URL="https://upload.pypi.org/legacy/" TWINE_REPOSITORY_URL=${TWINE_REPOSITORY_URL:="auto"} if [[ "${TWINE_REPOSITORY_URL}" == "auto" ]]; then - #if [[ "$(cat .git/HEAD)" != "ref: refs/heads/release" ]]; then + #if [[ "$(cat .git/HEAD)" != "ref: refs/heads/release" ]]; then # # If we are not on release, then default to the test pypi upload repo # TWINE_REPOSITORY_URL=${TWINE_REPOSITORY_URL:="https://test.pypi.org/legacy/"} #else @@ -237,6 +252,7 @@ GPG_KEYID = '$GPG_KEYID' DO_UPLOAD=${DO_UPLOAD} DO_TAG=${DO_TAG} DO_GPG=${DO_GPG} +DO_OTS=${DO_OTS} DO_BUILD=${DO_BUILD} MODE_LIST_STR=${MODE_LIST_STR} " @@ -244,10 +260,10 @@ MODE_LIST_STR=${MODE_LIST_STR} # Verify that we want to tag if [[ "$DO_TAG" == "True" ]]; then - echo "About to tag VERSION='$VERSION'" + echo "About to tag VERSION='$VERSION'" else if [[ "$DO_TAG" == "False" ]]; then - echo "We are NOT about to tag VERSION='$VERSION'" + echo "We are NOT about to tag VERSION='$VERSION'" else # shellcheck disable=SC2162 read -p "Do you want to git tag and push version='$VERSION'? (input 'yes' to confirm)" ANS @@ -282,10 +298,10 @@ fi # Verify that we want to publish if [[ "$DO_UPLOAD" == "True" ]]; then - echo "About to directly publish VERSION='$VERSION'" + echo "About to directly publish VERSION='$VERSION'" else if [[ "$DO_UPLOAD" == "False" ]]; then - echo "We are NOT about to directly publish VERSION='$VERSION'" + echo "We are NOT about to directly publish VERSION='$VERSION'" else # shellcheck disable=SC2162 read -p "Are you ready to directly publish version='$VERSION'? ('yes' will twine upload)" ANS @@ -375,7 +391,7 @@ ls_array(){ } -WHEEL_PATHS=() +WHEEL_FPATHS=() for _MODE in "${MODE_LIST[@]}" do if [[ "$_MODE" == "sdist" ]]; then @@ -388,40 +404,40 @@ do echo "ERROR: bad mode" exit 1 fi - # hacky CONCAT because for some reason ls_array will return + # hacky CONCAT because for some reason ls_array will return # something that looks empty but has one empty element for new_item in "${_NEW_WHEEL_PATHS[@]}" do if [[ "$new_item" != "" ]]; then - WHEEL_PATHS+=("$new_item") + WHEEL_FPATHS+=("$new_item") fi done done # Dedup the paths -readarray -t WHEEL_PATHS < <(printf '%s\n' "${WHEEL_PATHS[@]}" | sort -u) +readarray -t WHEEL_FPATHS < <(printf '%s\n' "${WHEEL_FPATHS[@]}" | sort -u) -WHEEL_PATHS_STR=$(printf '"%s" ' "${WHEEL_PATHS[@]}") +WHEEL_PATHS_STR=$(printf '"%s" ' "${WHEEL_FPATHS[@]}") echo "WHEEL_PATHS_STR = $WHEEL_PATHS_STR" echo " MODE=$MODE VERSION='$VERSION' -WHEEL_PATHS='$WHEEL_PATHS_STR' +WHEEL_FPATHS='$WHEEL_PATHS_STR' " - +WHEEL_SIGNATURE_FPATHS=() if [ "$DO_GPG" == "True" ]; then echo " === === " - for WHEEL_PATH in "${WHEEL_PATHS[@]}" + for WHEEL_FPATH in "${WHEEL_FPATHS[@]}" do - echo "WHEEL_PATH = $WHEEL_PATH" - check_variable WHEEL_PATH + echo "WHEEL_FPATH = $WHEEL_FPATH" + check_variable WHEEL_FPATH # https://stackoverflow.com/questions/45188811/how-to-gpg-sign-a-file-that-is-built-by-travis-ci # secure gpg --export-secret-keys > all.gpg @@ -432,13 +448,15 @@ if [ "$DO_GPG" == "True" ]; then echo "Signing wheels" GPG_SIGN_CMD="$GPG_EXECUTABLE --batch --yes --detach-sign --armor --local-user $GPG_KEYID" echo "GPG_SIGN_CMD = $GPG_SIGN_CMD" - $GPG_SIGN_CMD --output "$WHEEL_PATH".asc "$WHEEL_PATH" + $GPG_SIGN_CMD --output "$WHEEL_FPATH".asc "$WHEEL_FPATH" echo "Checking wheels" - twine check "$WHEEL_PATH".asc "$WHEEL_PATH" || { echo 'could not check wheels' ; exit 1; } + twine check "$WHEEL_FPATH".asc "$WHEEL_FPATH" || { echo 'could not check wheels' ; exit 1; } echo "Verifying wheels" - $GPG_EXECUTABLE --verify "$WHEEL_PATH".asc "$WHEEL_PATH" || { echo 'could not verify wheels' ; exit 1; } + $GPG_EXECUTABLE --verify "$WHEEL_FPATH".asc "$WHEEL_FPATH" || { echo 'could not verify wheels' ; exit 1; } + + WHEEL_SIGNATURE_FPATHS+=("$WHEEL_FPATH".asc) done echo " === === @@ -448,14 +466,35 @@ else fi + +if [ "$DO_OTS" == "True" ]; then + + echo " + === === + " + if [ "$DO_GPG" == "True" ]; then + # Stamp the wheels and the signatures + ots stamp "${WHEEL_FPATHS[@]}" "${WHEEL_SIGNATURE_FPATHS[@]}" + else + # Stamp only the wheels + ots stamp "${WHEEL_FPATHS[@]}" + fi + echo " + === === + " +else + echo "DO_OTS=False, Skipping OTS sign" +fi + + if [[ "$DO_TAG" == "True" ]]; then TAG_NAME="v${VERSION}" # if we messed up we can delete the tag # git push origin :refs/tags/$TAG_NAME # and then tag with -f - # + # git tag "$TAG_NAME" -m "tarball tag $VERSION" - git push --tags $DEPLOY_REMOTE + git push --tags "$DEPLOY_REMOTE" echo "Should also do a: git push $DEPLOY_REMOTE main:release" echo "For github should draft a new release: https://github.com/PyUtils/line_profiler/releases/new" else @@ -467,17 +506,11 @@ if [[ "$DO_UPLOAD" == "True" ]]; then check_variable TWINE_USERNAME check_variable TWINE_PASSWORD "hide" - for WHEEL_PATH in "${WHEEL_PATHS[@]}" + for WHEEL_FPATH in "${WHEEL_FPATHS[@]}" do - if [ "$DO_GPG" == "True" ]; then - twine upload --username "$TWINE_USERNAME" --password=$TWINE_PASSWORD \ - --repository-url "$TWINE_REPOSITORY_URL" \ - --sign "$WHEEL_PATH".asc "$WHEEL_PATH" --skip-existing --verbose || { echo 'failed to twine upload' ; exit 1; } - else - twine upload --username "$TWINE_USERNAME" --password=$TWINE_PASSWORD \ - --repository-url "$TWINE_REPOSITORY_URL" \ - "$WHEEL_PATH" --skip-existing --verbose || { echo 'failed to twine upload' ; exit 1; } - fi + twine upload --username "$TWINE_USERNAME" "--password=$TWINE_PASSWORD" \ + --repository-url "$TWINE_REPOSITORY_URL" \ + "$WHEEL_FPATH" --skip-existing --verbose || { echo 'failed to twine upload' ; exit 1; } done echo """ !!! FINISH: LIVE RUN !!! @@ -488,7 +521,7 @@ else DEPLOY_REMOTE = '$DEPLOY_REMOTE' DO_UPLOAD = '$DO_UPLOAD' - WHEEL_PATH = '$WHEEL_PATH' + WHEEL_FPATH = '$WHEEL_FPATH' WHEEL_PATHS_STR = '$WHEEL_PATHS_STR' MODE_LIST_STR = '$MODE_LIST_STR' @@ -502,3 +535,39 @@ else !!! FINISH: DRY RUN !!! """ fi + +__devel__=' +# Checking to see how easy it is to upload packages to gitlab. +# This logic should go in the CI script, not sure if it belongs here. + + +export HOST=https://gitlab.kitware.com +export GROUP_NAME=computer-vision +export PROJECT_NAME=geowatch +PROJECT_VERSION=$(geowatch --version) +echo "$PROJECT_VERSION" + +load_secrets +export PRIVATE_GITLAB_TOKEN=$(git_token_for "$HOST") +TMP_DIR=$(mktemp -d -t ci-XXXXXXXXXX) + +curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups" > "$TMP_DIR/all_group_info" +GROUP_ID=$(cat "$TMP_DIR/all_group_info" | jq ". | map(select(.name==\"$GROUP_NAME\")) | .[0].id") +echo "GROUP_ID = $GROUP_ID" + +curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" "$HOST/api/v4/groups/$GROUP_ID" > "$TMP_DIR/group_info" +PROJ_ID=$(cat "$TMP_DIR/group_info" | jq ".projects | map(select(.name==\"$PROJECT_NAME\")) | .[0].id") +echo "PROJ_ID = $PROJ_ID" + +ls_array DIST_FPATHS "dist/*" + +for FPATH in "${DIST_FPATHS[@]}" +do + FNAME=$(basename $FPATH) + echo $FNAME + curl --header "PRIVATE-TOKEN: $PRIVATE_GITLAB_TOKEN" \ + --upload-file $FPATH \ + "https://gitlab.kitware.com/api/v4/projects/$PROJ_ID/packages/generic/$PROJECT_NAME/$PROJECT_VERSION/$FNAME" +done + +' diff --git a/pyproject.toml b/pyproject.toml index 1b36b9b..37d8e74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,21 +13,27 @@ repo_name = "plottool_ibeis" os = [ "win", "linux", "osx",] ci_pypy_versions = [] # ci_cpython_versions = ["3.7", "3.8", "3.9", "3.10"] -supported_python_versions = ["3.7", "3.8", "3.9", "3.10"] -description = "Plottool - tools matplotlib computer vision plots" +# supported_python_versions = ["3.8", "3.9", "3.10", "3.11", "3.12"] +description = "Plottool - tools for matplotlib computer vision plots" url="https://github.com/Erotemic/plottool_ibeis" author="Jon Crall" author_email="erotemic@gmail.com" -min_python = 3.7 +min_python = 3.8 version = "{mod_dpath}/__init__.py::__version__" license = "Apache 2" dev_status = "beta" autostage = true [tool.pytest.ini_options] -addopts = "-p no:doctest --xdoctest --xdoctest-style=google --ignore-glob=setup.py" -norecursedirs = ".git ignore build __pycache__ dev _skbuild" -filterwarnings = [ "default", "ignore:.*No cfgstr given in Cacher constructor or call.*:Warning", "ignore:.*Define the __nice__ method for.*:Warning", "ignore:.*private pytest class or function.*:Warning",] +addopts = "-p no:doctest --xdoctest --xdoctest-style=google --ignore-glob=setup.py --ignore-glob=docs" +norecursedirs = ".git ignore build __pycache__ dev _skbuild docs" +filterwarnings = [ + "default", + "ignore:.*No cfgstr given in Cacher constructor or call.*:Warning", + "ignore:.*Define the __nice__ method for.*:Warning", + "ignore:.*private pytest class or function.*:Warning", +] + [tool.coverage.run] branch = true diff --git a/requirements/graphics.txt b/requirements/graphics.txt index 57af9e0..b918a95 100644 --- a/requirements/graphics.txt +++ b/requirements/graphics.txt @@ -1,5 +1,7 @@ -# python ~/local/tools/supported_python_versions_pip.py opencv-python -opencv-python>=4.5.4.58 ; python_version >= '3.10' # Python 3.10+ +# xdev availpkg opencv-python-headless +# --prefer-binary +opencv-python>=4.5.5.64 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+ +opencv-python>=4.5.4.58 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 opencv-python>=3.4.15.55 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 opencv-python>=3.4.15.55 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 opencv-python>=3.4.15.55 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 diff --git a/requirements/headless.txt b/requirements/headless.txt index 14f0c3f..5aeaa00 100644 --- a/requirements/headless.txt +++ b/requirements/headless.txt @@ -1,9 +1,11 @@ -# python ~/local/tools/supported_python_versions_pip.py opencv-python-headless -opencv-python-headless>=4.5.4.58 ; python_version >= '3.10' # Python 3.10+ -opencv-python-headless>=3.4.13.47 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 -opencv-python-headless>=3.4.13.47 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 -opencv-python-headless>=3.4.13.47 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 +# xdev availpkg opencv-python-headless +# --prefer-binary +opencv-python-headless>=4.5.5.64 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+ +opencv-python-headless>=4.5.4.58 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 +opencv-python-headless>=3.4.15.55 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 +opencv-python-headless>=3.4.15.55 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 +opencv-python-headless>=3.4.15.55 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 opencv-python-headless>=3.4.13.47 ; python_version < '3.7' and python_version >= '3.6' # Python 3.6 -opencv-python-headless>=3.4.11.39 ; python_version < '3.6' and python_version >= '3.5' # Python 3.5 +opencv-python-headless>=3.4.2.16 ; python_version < '3.6' and python_version >= '3.5' # Python 3.5 opencv-python-headless>=3.4.2.16 ; python_version < '3.5' and python_version >= '3.4' # Python 3.4 opencv-python-headless>=3.4.2.16 ; python_version < '3.4' and python_version >= '2.7' # Python 2.7 diff --git a/requirements/optional.txt b/requirements/optional.txt index bfdf8fb..64bf09f 100644 --- a/requirements/optional.txt +++ b/requirements/optional.txt @@ -1 +1,2 @@ -pyqt5>=5.15.5 +pyqt5>=5.15.10 ; python_version < '4.0' and python_version >= '3.12' # Python 3.12 +pyqt5>=5.15.5 ; python_version < '3.12' and python_version >= '2.7' # Python 3.11- diff --git a/requirements/runtime.txt b/requirements/runtime.txt index fbc91ca..657e678 100644 --- a/requirements/runtime.txt +++ b/requirements/runtime.txt @@ -1,16 +1,19 @@ -six >= 1.10.0 -ubelt >= 1.2.2 +six>=1.16.0 ; python_version < '4.0' and python_version >= '3.12' # Python 3.12+ +six>=1.10.0 ; python_version < '3.12' # Python 3.11- -matplotlib>=3.5.0 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+ +ubelt >= 1.3.4 + +matplotlib>=3.8.4 ; python_version < '4.0' and python_version >= '3.12' # Python 3.12 +matplotlib>=3.5.0 ; python_version < '3.12' and python_version >= '3.11' # Python 3.11 matplotlib>=3.5.0 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 matplotlib>=3.3.3 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 matplotlib>=3.6.0 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 matplotlib>=2.2.2 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 matplotlib>=1.5.3 ; python_version < '3.7' and python_version >= '3.6' # Python 3.6 -utool >= 2.1.5 -vtool_ibeis >= 2.2.0 -guitool_ibeis >= 2.1.0 +utool >= 2.2.0 +vtool_ibeis >= 2.3.0 +guitool_ibeis >= 2.2.0 cachetools>=5.0.0 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+ cachetools>=5.0.0 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 @@ -18,3 +21,11 @@ cachetools>=4.2.0 ; python_version < '3.10' and python_version >= '3.9' cachetools>=4.0.0 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 cachetools>=5.0.0 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 cachetools>=4.0.0 ; python_version < '3.7' and python_version >= '3.6' # Python 3.6 + +numpy>=1.26.0 ; python_version < '4.0' and python_version >= '3.12' # Python 3.12+ +numpy>=1.24.0 ; python_version < '3.12' and python_version >= '3.11' # Python 3.11 +numpy>=1.21.6 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 +numpy>=1.19.3 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 +numpy>=1.19.2 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 +numpy>=1.19.2 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 +numpy>=1.19.2 ; python_version < '3.7' and python_version >= '3.6' # Python 3.6 diff --git a/requirements/tests.txt b/requirements/tests.txt index abd5423..ceedd54 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,13 +1,17 @@ -xdoctest>=0.14.0 +xdoctest >= 1.1.3 # Pin maximum pytest versions for older python versions # TODO: determine what the actual minimum and maximum acceptable versions of # pytest (that are also compatible with xdoctest) are for each legacy python # major.minor version. # See ~/local/tools/supported_python_versions_pip.py for helper script -pytest>=6.2.5 ; python_version >= '3.10.0' # Python 3.10+ -pytest>=4.6.0 ; python_version < '3.10.0' and python_version >= '3.7.0' # Python 3.7-3.9 -pytest>=4.6.0 ; python_version < '3.7.0' and python_version >= '3.6.0' # Python 3.6 +pytest>=8.1.1 ; python_version < '4.0' and python_version >= '3.13' # Python 3.13+ +pytest>=8.1.1 ; python_version < '3.13' and python_version >= '3.12' # Python 3.12 +pytest>=8.1.1 ; python_version < '3.12' and python_version >= '3.11' # Python 3.11 +pytest>=8.1.1 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10 +pytest>=8.1.1 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9 +pytest>=8.1.1 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8 +pytest>=4.6.0 ; python_version < '3.8' and python_version >= '3.7' # Python 3.7 pytest>=4.6.0, <= 6.1.2 ; python_version < '3.6.0' and python_version >= '3.5.0' # Python 3.5 pytest>=4.6.0, <= 4.6.11 ; python_version < '3.5.0' and python_version >= '3.4.0' # Python 3.4 pytest>=4.6.0, <= 4.6.11 ; python_version < '2.8.0' and python_version >= '2.7.0' # Python 2.7 @@ -22,7 +26,6 @@ pytest-cov>=2.8.1 ; python_version < '3.5.0' and python_version >= '3 pytest-cov>=2.8.1 ; python_version < '2.8.0' and python_version >= '2.7.0' # Python 2.7 # python ~/local/tools/supported_python_versions_pip.py pytest-timeout -# python ~/local/tools/supported_python_versions_pip.py codecov pytest-timeout>=1.4.2 # python ~/local/tools/supported_python_versions_pip.py xdoctest @@ -37,8 +40,4 @@ coverage>=4.3.4 ; python_version < '3.5' and python_version >= '3.4' # Py coverage>=5.3.1 ; python_version < '3.4' and python_version >= '2.7' # Python 2.7 coverage>=4.5 ; python_version < '2.7' and python_version >= '2.6' # Python 2.6 -codecov>=2.0.15 - -requests>=2.25.1 - -pyqt5>=5.15.5 +requests >= 2.27.1 diff --git a/run_linter.sh b/run_linter.sh index 8f85927..9cf1554 100755 --- a/run_linter.sh +++ b/run_linter.sh @@ -1,2 +1,3 @@ #!/bin/bash -flake8 ./plottool_ibeis --count --select=E9,F63,F7,F82 --show-source --statistics +flake8 --count --select=E9,F63,F7,F82 --show-source --statistics plottool_ibeis +flake8 --count --select=E9,F63,F7,F82 --show-source --statistics ./tests \ No newline at end of file diff --git a/setup.py b/setup.py index 99440e0..9ea8dbb 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,8 @@ # Generated by ~/code/xcookie/xcookie/builders/setup.py # based on part ~/code/xcookie/xcookie/rc/setup.py.in import sys -from os.path import exists +import re +from os.path import exists, dirname, join from setuptools import find_packages from setuptools import setup @@ -53,8 +54,6 @@ def parse_description(): pandoc --from=markdown --to=rst --output=README.rst README.md python -c "import setup; print(setup.parse_description())" """ - from os.path import dirname, join, exists - readme_fpath = join(dirname(__file__), "README.rst") # This breaks on pip install, so check that it exists. if exists(readme_fpath): @@ -77,10 +76,10 @@ def parse_requirements(fname="requirements.txt", versions=False): Returns: List[str]: list of requirements items - """ - from os.path import exists, dirname, join - import re + CommandLine: + python -c "import setup, ubelt; print(ubelt.urepr(setup.parse_requirements()))" + """ require_fpath = fname def parse_line(line, dpath=""): @@ -198,56 +197,65 @@ def gen_packages_items(): NAME = "plottool_ibeis" INIT_PATH = "plottool_ibeis/__init__.py" -VERSION = parse_version("plottool_ibeis/__init__.py") +VERSION = parse_version(INIT_PATH) if __name__ == "__main__": setupkw = {} - setupkw["install_requires"] = parse_requirements("requirements/runtime.txt") + setupkw["install_requires"] = parse_requirements( + "requirements/runtime.txt", versions="loose" + ) setupkw["extras_require"] = { - "all": parse_requirements("requirements.txt"), - "tests": parse_requirements("requirements/tests.txt"), - "optional": parse_requirements("requirements/optional.txt"), - "headless": parse_requirements("requirements/headless.txt"), - "graphics": parse_requirements("requirements/graphics.txt"), - # Strict versions + "all": parse_requirements("requirements.txt", versions="loose"), + "headless": parse_requirements("requirements/headless.txt", versions="loose"), + "graphics": parse_requirements("requirements/graphics.txt", versions="loose"), + "docs": parse_requirements("requirements/docs.txt", versions="loose"), + "optional": parse_requirements("requirements/optional.txt", versions="loose"), + "problematic": parse_requirements( + "requirements/problematic.txt", versions="loose" + ), + "runtime": parse_requirements("requirements/runtime.txt", versions="loose"), + "tests": parse_requirements("requirements/tests.txt", versions="loose"), + "all-strict": parse_requirements("requirements.txt", versions="strict"), "headless-strict": parse_requirements( "requirements/headless.txt", versions="strict" ), "graphics-strict": parse_requirements( "requirements/graphics.txt", versions="strict" ), - "all-strict": parse_requirements("requirements.txt", versions="strict"), + "docs-strict": parse_requirements("requirements/docs.txt", versions="strict"), + "optional-strict": parse_requirements( + "requirements/optional.txt", versions="strict" + ), + "problematic-strict": parse_requirements( + "requirements/problematic.txt", versions="strict" + ), "runtime-strict": parse_requirements( "requirements/runtime.txt", versions="strict" ), "tests-strict": parse_requirements("requirements/tests.txt", versions="strict"), - "optional-strict": parse_requirements( - "requirements/optional.txt", versions="strict" - ), } - setupkw["name"] = NAME setupkw["version"] = VERSION setupkw["author"] = "Jon Crall" setupkw["author_email"] = "erotemic@gmail.com" setupkw["url"] = "https://github.com/Erotemic/plottool_ibeis" - setupkw["description"] = "Plottool - tools matplotlib computer vision plots" + setupkw["description"] = "Plottool - tools for matplotlib computer vision plots" setupkw["long_description"] = parse_description() setupkw["long_description_content_type"] = "text/x-rst" setupkw["license"] = "Apache 2" setupkw["packages"] = find_packages(".") - setupkw["python_requires"] = ">=3.7" + setupkw["python_requires"] = ">=3.8" setupkw["classifiers"] = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] setup(**setupkw)