From 17c4ed64b9c67f8be626249bbedb4a378728e63f Mon Sep 17 00:00:00 2001 From: Charles Whittington Date: Sat, 18 Jan 2025 13:03:14 -0500 Subject: [PATCH 1/4] Added archive note to readme --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 6187b7b..950fc46 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,9 @@ :target: https://beeware.org/bee/chat/ :alt: Discord server +**NOTE:** This repository is archived. Current development of Travertino has moved to +the `Toga repository`_. + |logo| Travertino @@ -90,6 +93,7 @@ the `Briefcase code contribution guide Although that document is for a different project, the details about setting up your development environment are the same. +.. _Toga repository: https://github.com/beeware/toga .. _BeeWare suite: https://beeware.org .. _Read The Docs: https://travertino.readthedocs.io .. _BeeWare Community Code of Conduct: https://beeware.org/community/behavior/ From 0367536c05080b462b9ad4536ba74ce669246099 Mon Sep 17 00:00:00 2001 From: Charles Whittington Date: Sat, 18 Jan 2025 13:06:14 -0500 Subject: [PATCH 2/4] Added changenote --- changes/249.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/249.misc.rst diff --git a/changes/249.misc.rst b/changes/249.misc.rst new file mode 100644 index 0000000..8a7106c --- /dev/null +++ b/changes/249.misc.rst @@ -0,0 +1 @@ +The readme has been edited to reflect that Travertino development has moved to the Toga repository. From 5a8732ad25b88d63bd5dec726110732083100b5d Mon Sep 17 00:00:00 2001 From: Charles Whittington Date: Sun, 19 Jan 2025 16:34:42 -0500 Subject: [PATCH 3/4] Removed everything except the readme --- .git-blame-ignore-revs | 2 - .github/dependabot.yml | 18 - .github/workflows/ci.yml | 78 -- .github/workflows/config-file-deps-bump.yml | 12 - .github/workflows/dependabot-changenote.yml | 12 - .github/workflows/pre-commit-update.yml | 12 - .github/workflows/publish.yml | 22 - .github/workflows/release.yml | 60 -- .gitignore | 26 - .pre-commit-config.yaml | 28 - AUTHORS | 8 - CHANGELOG.rst | 85 -- CONTRIBUTING.md | 7 - LICENSE | 27 - README.rst | 97 +-- changes/.gitignore | 1 - changes/100.misc.rst | 1 - changes/101.misc.rst | 1 - changes/102.misc.rst | 1 - changes/103.misc.rst | 1 - changes/104.misc.rst | 1 - changes/105.misc.rst | 1 - changes/106.misc.rst | 1 - changes/107.misc.rst | 1 - changes/108.misc.rst | 1 - changes/109.misc.rst | 1 - changes/110.misc.rst | 1 - changes/111.misc.rst | 1 - changes/112.misc.rst | 1 - changes/113.misc.rst | 1 - changes/114.misc.rst | 1 - changes/115.misc.rst | 1 - changes/116.misc.rst | 1 - changes/117.misc.rst | 1 - changes/118.misc.rst | 1 - changes/120.misc.rst | 1 - changes/121.misc.rst | 1 - changes/122.misc.rst | 1 - changes/123.misc.rst | 1 - changes/124.misc.rst | 1 - changes/125.misc.rst | 1 - changes/126.misc.rst | 1 - changes/127.misc.rst | 1 - changes/128.misc.rst | 1 - changes/129.misc.rst | 1 - changes/130.misc.rst | 1 - changes/131.misc.rst | 1 - changes/132.misc.rst | 1 - changes/133.misc.rst | 1 - changes/134.misc.rst | 1 - changes/135.misc.rst | 1 - changes/136.misc.rst | 1 - changes/137.misc.rst | 1 - changes/138.misc.rst | 1 - changes/139.removal.rst | 1 - changes/140.misc.rst | 1 - changes/141.feature.rst | 1 - changes/142.misc.rst | 1 - changes/143.feature.rst | 1 - changes/144.misc.rst | 1 - changes/145.misc.rst | 1 - changes/146.misc.rst | 1 - changes/147.misc.rst | 1 - changes/148.feature.rst | 1 - changes/149.feature.rst | 1 - changes/150.misc.rst | 1 - changes/151.misc.rst | 1 - changes/152.misc.rst | 1 - changes/154.misc.rst | 1 - changes/155.misc.rst | 1 - changes/156.misc.rst | 1 - changes/157.misc.rst | 1 - changes/158.misc.rst | 1 - changes/159.misc.rst | 1 - changes/160.misc.rst | 1 - changes/161.misc.rst | 1 - changes/162.misc.rst | 1 - changes/163.misc.rst | 1 - changes/164.misc.rst | 1 - changes/165.misc.rst | 1 - changes/166.misc.rst | 1 - changes/167.misc.rst | 1 - changes/168.misc.rst | 1 - changes/169.misc.rst | 1 - changes/170.doc.rst | 1 - changes/171.misc.rst | 1 - changes/172.misc.rst | 1 - changes/173.misc.rst | 1 - changes/174.misc.rst | 1 - changes/175.misc.rst | 1 - changes/176.misc.rst | 1 - changes/177.misc.rst | 1 - changes/178.misc.rst | 1 - changes/179.misc.rst | 1 - changes/180.misc.rst | 1 - changes/181.misc.rst | 1 - changes/182.misc.rst | 1 - changes/183.misc.rst | 1 - changes/184.misc.rst | 1 - changes/185.misc.rst | 1 - changes/186.misc.rst | 1 - changes/187.misc.rst | 1 - changes/188.misc.rst | 1 - changes/189.misc.rst | 1 - changes/190.misc.rst | 1 - changes/191.misc.rst | 1 - changes/192.misc.rst | 1 - changes/193.misc.rst | 1 - changes/194.misc.rst | 1 - changes/195.misc.rst | 1 - changes/196.misc.rst | 1 - changes/197.misc.rst | 1 - changes/199.misc.rst | 1 - changes/200.misc.rst | 1 - changes/202.misc.rst | 1 - changes/204.misc.rst | 1 - changes/205.misc.rst | 1 - changes/206.misc.rst | 1 - changes/207.misc.rst | 1 - changes/208.misc.rst | 1 - changes/209.misc.rst | 1 - changes/210.misc.rst | 1 - changes/211.misc.rst | 1 - changes/212.misc.rst | 1 - changes/213.misc.rst | 1 - changes/214.misc.rst | 1 - changes/215.misc.rst | 1 - changes/216.misc.rst | 1 - changes/217.misc.rst | 1 - changes/218.misc.rst | 1 - changes/219.misc.rst | 1 - changes/220.misc.rst | 1 - changes/221.misc.rst | 1 - changes/223.feature.rst | 1 - changes/223.removal.rst | 1 - changes/224.bugfix.rst | 1 - changes/224.misc.rst | 1 - changes/224.removal.1.rst | 1 - changes/224.removal.2.rst | 1 - changes/225.misc.rst | 1 - changes/226.misc.rst | 1 - changes/227.misc.rst | 1 - changes/228.misc.rst | 1 - changes/229.misc.rst | 1 - changes/230.misc.rst | 1 - changes/231.misc.rst | 1 - changes/232.misc.rst | 1 - changes/233.bugfix.rst | 1 - changes/234.misc.rst | 1 - changes/235.misc.rst | 1 - changes/236.misc.rst | 1 - changes/237.misc.rst | 1 - changes/238.misc.rst | 1 - changes/239.misc.rst | 1 - changes/240.misc.rst | 1 - changes/241.feature.rst | 1 - changes/242.misc.rst | 1 - changes/244.removal.rst | 1 - changes/245.misc.rst | 1 - changes/247.misc.rst | 1 - changes/248.misc.rst | 1 - changes/249.misc.rst | 1 - changes/88.misc.rst | 1 - changes/89.misc.rst | 1 - changes/90.misc.rst | 1 - changes/91.misc.rst | 1 - changes/92.misc.rst | 1 - changes/93.misc.rst | 1 - changes/94.misc.rst | 1 - changes/95.misc.rst | 1 - changes/96.misc.rst | 1 - changes/97.misc.rst | 1 - changes/98.misc.rst | 1 - changes/99.misc.rst | 1 - changes/template.rst | 32 - pyproject.toml | 82 -- src/travertino/__init__.py | 17 - src/travertino/colors.py | 403 ---------- src/travertino/constants.py | 264 ------- src/travertino/declaration.py | 457 ----------- src/travertino/fonts.py | 199 ----- src/travertino/layout.py | 170 ---- src/travertino/node.py | 196 ----- src/travertino/size.py | 68 -- tests/__init__.py | 0 tests/colors/__init__.py | 0 tests/colors/test_constructor.py | 170 ---- tests/colors/test_parsing.py | 171 ---- tests/fonts/__init__.py | 0 tests/fonts/test_constructor.py | 203 ----- tests/fonts/test_parsing.py | 133 ---- tests/test_choices.py | 385 --------- tests/test_declaration.py | 836 -------------------- tests/test_layout.py | 407 ---------- tests/test_node.py | 467 ----------- tests/test_size.py | 138 ---- tests/utils.py | 29 - tox.ini | 41 - 198 files changed, 2 insertions(+), 5519 deletions(-) delete mode 100644 .git-blame-ignore-revs delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/config-file-deps-bump.yml delete mode 100644 .github/workflows/dependabot-changenote.yml delete mode 100644 .github/workflows/pre-commit-update.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .gitignore delete mode 100644 .pre-commit-config.yaml delete mode 100644 AUTHORS delete mode 100644 CHANGELOG.rst delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE delete mode 100644 changes/.gitignore delete mode 100644 changes/100.misc.rst delete mode 100644 changes/101.misc.rst delete mode 100644 changes/102.misc.rst delete mode 100644 changes/103.misc.rst delete mode 100644 changes/104.misc.rst delete mode 100644 changes/105.misc.rst delete mode 100644 changes/106.misc.rst delete mode 100644 changes/107.misc.rst delete mode 100644 changes/108.misc.rst delete mode 100644 changes/109.misc.rst delete mode 100644 changes/110.misc.rst delete mode 100644 changes/111.misc.rst delete mode 100644 changes/112.misc.rst delete mode 100644 changes/113.misc.rst delete mode 100644 changes/114.misc.rst delete mode 100644 changes/115.misc.rst delete mode 100644 changes/116.misc.rst delete mode 100644 changes/117.misc.rst delete mode 100644 changes/118.misc.rst delete mode 100644 changes/120.misc.rst delete mode 100644 changes/121.misc.rst delete mode 100644 changes/122.misc.rst delete mode 100644 changes/123.misc.rst delete mode 100644 changes/124.misc.rst delete mode 100644 changes/125.misc.rst delete mode 100644 changes/126.misc.rst delete mode 100644 changes/127.misc.rst delete mode 100644 changes/128.misc.rst delete mode 100644 changes/129.misc.rst delete mode 100644 changes/130.misc.rst delete mode 100644 changes/131.misc.rst delete mode 100644 changes/132.misc.rst delete mode 100644 changes/133.misc.rst delete mode 100644 changes/134.misc.rst delete mode 100644 changes/135.misc.rst delete mode 100644 changes/136.misc.rst delete mode 100644 changes/137.misc.rst delete mode 100644 changes/138.misc.rst delete mode 100644 changes/139.removal.rst delete mode 100644 changes/140.misc.rst delete mode 100644 changes/141.feature.rst delete mode 100644 changes/142.misc.rst delete mode 100644 changes/143.feature.rst delete mode 100644 changes/144.misc.rst delete mode 100644 changes/145.misc.rst delete mode 100644 changes/146.misc.rst delete mode 100644 changes/147.misc.rst delete mode 100644 changes/148.feature.rst delete mode 100644 changes/149.feature.rst delete mode 100644 changes/150.misc.rst delete mode 100644 changes/151.misc.rst delete mode 100644 changes/152.misc.rst delete mode 100644 changes/154.misc.rst delete mode 100644 changes/155.misc.rst delete mode 100644 changes/156.misc.rst delete mode 100644 changes/157.misc.rst delete mode 100644 changes/158.misc.rst delete mode 100644 changes/159.misc.rst delete mode 100644 changes/160.misc.rst delete mode 100644 changes/161.misc.rst delete mode 100644 changes/162.misc.rst delete mode 100644 changes/163.misc.rst delete mode 100644 changes/164.misc.rst delete mode 100644 changes/165.misc.rst delete mode 100644 changes/166.misc.rst delete mode 100644 changes/167.misc.rst delete mode 100644 changes/168.misc.rst delete mode 100644 changes/169.misc.rst delete mode 100644 changes/170.doc.rst delete mode 100644 changes/171.misc.rst delete mode 100644 changes/172.misc.rst delete mode 100644 changes/173.misc.rst delete mode 100644 changes/174.misc.rst delete mode 100644 changes/175.misc.rst delete mode 100644 changes/176.misc.rst delete mode 100644 changes/177.misc.rst delete mode 100644 changes/178.misc.rst delete mode 100644 changes/179.misc.rst delete mode 100644 changes/180.misc.rst delete mode 100644 changes/181.misc.rst delete mode 100644 changes/182.misc.rst delete mode 100644 changes/183.misc.rst delete mode 100644 changes/184.misc.rst delete mode 100644 changes/185.misc.rst delete mode 100644 changes/186.misc.rst delete mode 100644 changes/187.misc.rst delete mode 100644 changes/188.misc.rst delete mode 100644 changes/189.misc.rst delete mode 100644 changes/190.misc.rst delete mode 100644 changes/191.misc.rst delete mode 100644 changes/192.misc.rst delete mode 100644 changes/193.misc.rst delete mode 100644 changes/194.misc.rst delete mode 100644 changes/195.misc.rst delete mode 100644 changes/196.misc.rst delete mode 100644 changes/197.misc.rst delete mode 100644 changes/199.misc.rst delete mode 100644 changes/200.misc.rst delete mode 100644 changes/202.misc.rst delete mode 100644 changes/204.misc.rst delete mode 100644 changes/205.misc.rst delete mode 100644 changes/206.misc.rst delete mode 100644 changes/207.misc.rst delete mode 100644 changes/208.misc.rst delete mode 100644 changes/209.misc.rst delete mode 100644 changes/210.misc.rst delete mode 100644 changes/211.misc.rst delete mode 100644 changes/212.misc.rst delete mode 100644 changes/213.misc.rst delete mode 100644 changes/214.misc.rst delete mode 100644 changes/215.misc.rst delete mode 100644 changes/216.misc.rst delete mode 100644 changes/217.misc.rst delete mode 100644 changes/218.misc.rst delete mode 100644 changes/219.misc.rst delete mode 100644 changes/220.misc.rst delete mode 100644 changes/221.misc.rst delete mode 100644 changes/223.feature.rst delete mode 100644 changes/223.removal.rst delete mode 100644 changes/224.bugfix.rst delete mode 100644 changes/224.misc.rst delete mode 100644 changes/224.removal.1.rst delete mode 100644 changes/224.removal.2.rst delete mode 100644 changes/225.misc.rst delete mode 100644 changes/226.misc.rst delete mode 100644 changes/227.misc.rst delete mode 100644 changes/228.misc.rst delete mode 100644 changes/229.misc.rst delete mode 100644 changes/230.misc.rst delete mode 100644 changes/231.misc.rst delete mode 100644 changes/232.misc.rst delete mode 100644 changes/233.bugfix.rst delete mode 100644 changes/234.misc.rst delete mode 100644 changes/235.misc.rst delete mode 100644 changes/236.misc.rst delete mode 100644 changes/237.misc.rst delete mode 100644 changes/238.misc.rst delete mode 100644 changes/239.misc.rst delete mode 100644 changes/240.misc.rst delete mode 100644 changes/241.feature.rst delete mode 100644 changes/242.misc.rst delete mode 100644 changes/244.removal.rst delete mode 100644 changes/245.misc.rst delete mode 100644 changes/247.misc.rst delete mode 100644 changes/248.misc.rst delete mode 100644 changes/249.misc.rst delete mode 100644 changes/88.misc.rst delete mode 100644 changes/89.misc.rst delete mode 100644 changes/90.misc.rst delete mode 100644 changes/91.misc.rst delete mode 100644 changes/92.misc.rst delete mode 100644 changes/93.misc.rst delete mode 100644 changes/94.misc.rst delete mode 100644 changes/95.misc.rst delete mode 100644 changes/96.misc.rst delete mode 100644 changes/97.misc.rst delete mode 100644 changes/98.misc.rst delete mode 100644 changes/99.misc.rst delete mode 100644 changes/template.rst delete mode 100644 pyproject.toml delete mode 100644 src/travertino/__init__.py delete mode 100644 src/travertino/colors.py delete mode 100644 src/travertino/constants.py delete mode 100644 src/travertino/declaration.py delete mode 100644 src/travertino/fonts.py delete mode 100644 src/travertino/layout.py delete mode 100644 src/travertino/node.py delete mode 100644 src/travertino/size.py delete mode 100644 tests/__init__.py delete mode 100644 tests/colors/__init__.py delete mode 100644 tests/colors/test_constructor.py delete mode 100644 tests/colors/test_parsing.py delete mode 100644 tests/fonts/__init__.py delete mode 100644 tests/fonts/test_constructor.py delete mode 100644 tests/fonts/test_parsing.py delete mode 100644 tests/test_choices.py delete mode 100644 tests/test_declaration.py delete mode 100644 tests/test_layout.py delete mode 100644 tests/test_node.py delete mode 100644 tests/test_size.py delete mode 100644 tests/utils.py delete mode 100644 tox.ini diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index fd0ec73..0000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1,2 +0,0 @@ -# Initial application of pre-commit (including Black) -77f65d1a2ebbb8593e6968ce1af212693b074c93 diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 3b302bb..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,18 +0,0 @@ - -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - # Check for updates on Sunday, 8PM UTC - interval: "weekly" - day: "sunday" - time: "20:00" - - - package-ecosystem: "pip" - directory: "/" - schedule: - # Check for updates on Sunday, 8PM UTC - interval: "weekly" - day: "sunday" - time: "20:00" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a48fcf7..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: CI -on: - pull_request: - push: - branches: - - main - workflow_call: - inputs: - attest-package: - description: "Create GitHub provenance attestation for the package." - default: "false" - type: string - outputs: - artifact-name: - description: "Name of the uploaded artifact; use for artifact retrieval." - value: ${{ jobs.package.outputs.artifact-name }} - -jobs: - pre-commit: - name: Pre-commit checks - uses: beeware/.github/.github/workflows/pre-commit-run.yml@main - - towncrier: - name: Check towncrier - uses: beeware/.github/.github/workflows/towncrier-run.yml@main - - package: - name: Package Travertino - permissions: - id-token: write - contents: read - attestations: write - uses: beeware/.github/.github/workflows/python-package-create.yml@main - with: - attest: ${{ inputs.attest-package }} - - unit-tests: - name: Python compatibility test - needs: [ pre-commit, towncrier, package ] - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental }} - strategy: - fail-fast: false - matrix: - python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ] - include: - # Builds must pass by default - - experimental: false - - # Development Python can fail without failing the entire job - - python-version: "3.14" - experimental: true - steps: - - name: Checkout - uses: actions/checkout@v4.2.2 - with: - fetch-depth: 0 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5.3.0 - with: - python-version: ${{ matrix.python-version }} - allow-prereleases: true - - - name: Get Packages - uses: actions/download-artifact@v4.1.8 - with: - name: ${{ needs.package.outputs.artifact-name }} - path: dist - - - name: Install Tox - uses: beeware/.github/.github/actions/install-requirement@main - with: - requirements: tox - extra: dev - - - name: Test - run: tox -e py --installpkg dist/travertino-*.whl diff --git a/.github/workflows/config-file-deps-bump.yml b/.github/workflows/config-file-deps-bump.yml deleted file mode 100644 index 5a9040b..0000000 --- a/.github/workflows/config-file-deps-bump.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Bump Config File Dependencies - -on: - schedule: - - cron: "0 20 * * SUN" # Sunday @ 2000 UTC - workflow_dispatch: - -jobs: - dep-bump-versions: - name: Bump Config File Dependencies - uses: beeware/.github/.github/workflows/dep-version-bump.yml@main - secrets: inherit diff --git a/.github/workflows/dependabot-changenote.yml b/.github/workflows/dependabot-changenote.yml deleted file mode 100644 index 17848bc..0000000 --- a/.github/workflows/dependabot-changenote.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Dependabot Change Note - -on: - push: - branches: - - 'dependabot/**' - -jobs: - changenote: - name: Dependabot Change Note - uses: beeware/.github/.github/workflows/dependabot-changenote.yml@main - secrets: inherit diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml deleted file mode 100644 index 2bc6a36..0000000 --- a/.github/workflows/pre-commit-update.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Update pre-commit - -on: - schedule: - - cron: "0 20 * * SUN" # Sunday @ 2000 UTC - workflow_dispatch: - -jobs: - pre-commit-update: - name: Update pre-commit - uses: beeware/.github/.github/workflows/pre-commit-update.yml@main - secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 1733a2b..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Upload Python Package - -on: - release: - types: published - -jobs: - deploy: - runs-on: ubuntu-latest - permissions: - # This permission is required for trusted publishing. - id-token: write - steps: - - uses: dsaltares/fetch-gh-release-asset@1.1.2 - with: - version: tags/${{ github.event.release.tag_name }} - file: ${{ github.event.repository.name }}.* - regex: true - target: dist/ - - - name: Publish release to production PyPI - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 552e9da..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Create Release - -on: - push: - tags: - - "v*" - -jobs: - ci: - name: CI - uses: ./.github/workflows/ci.yml - with: - attest-package: "true" - - release: - name: Create Release - needs: ci - runs-on: ubuntu-latest - permissions: - contents: write - # This permission is required for trusted publishing. - id-token: write - steps: - - name: Set build variables - run: | - echo "VERSION=${GITHUB_REF_NAME#v}" | tee -a $GITHUB_ENV - - - name: Set up Python - uses: actions/setup-python@v5.3.0 - with: - python-version: "3.x" - - - name: Get packages - uses: actions/download-artifact@v4.1.8 - with: - name: ${{ needs.ci.outputs.artifact-name }} - path: dist - - - name: Install packages - run: pip install dist/*.whl - - - name: Check version number - # Check that the setuptools_scm-generated version number is still the same when - # installed from a wheel with setuptools_scm not present. - run: | - set -x - test $(python -c "from travertino import __version__; print(__version__)") = $VERSION - - - name: Create release - uses: ncipollo/release-action@v1.15.0 - with: - name: ${{ env.VERSION }} - draft: true - artifacts: dist/* - artifactErrorsFailBuild: true - - - name: Publish release to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository_url: https://test.pypi.org/legacy/ diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 4faa3d5..0000000 --- a/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -*.pyc -*~ -.*.sw[op] -*.egg-info -*.dist-info -.eggs -.coverage -.coverage.* -coverage.xml -dist -build -logs -_build -distribute-* -docs/env -local -*venv* -.idea -Pipfile* -.tox -.DS_Store -pip-selfcheck.json -pyvenv.cfg -.vscode -.envrc -pip-wheel-metadata diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 78e0831..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,28 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: check-toml - - id: check-yaml - - id: check-case-conflict - - id: check-docstring-first - - id: end-of-file-fixer - - id: trailing-whitespace - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - additional_dependencies: [toml] - - repo: https://github.com/asottile/pyupgrade - rev: v3.19.1 - hooks: - - id: pyupgrade - args: [--py39-plus] - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black - - repo: https://github.com/PyCQA/flake8 - rev: 7.1.1 - hooks: - - id: flake8 diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 017082d..0000000 --- a/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -Travertino was originally created in December 2017. - -The PRIMARY AUTHORS are (and/or have been): - Russell Keith-Magee - -And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- -people who have submitted patches, reported bugs, added translations, helped -answer newbie questions, and generally made Travertino that much better: diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index f102040..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,85 +0,0 @@ -Changelog -========= - -.. towncrier release notes start - -0.3.0 (2023-08-16) -================== - -Features --------- - -* Layout nodes can now track the minimum permitted layout size in addition to the current actual layout size. (`#78 `_) - - -Backward Incompatible Changes ------------------------------ - -* Support for Python 3.7 was removed. (`#80 `_) - - -Misc ----- - -* `#44 `_, `#45 `_, `#46 `_, `#47 `_, `#48 `_, `#49 `_, `#50 `_, `#51 `_, `#52 `_, `#53 `_, `#54 `_, `#55 `_, `#56 `_, `#57 `_, `#58 `_, `#59 `_, `#60 `_, `#61 `_, `#62 `_, `#63 `_, `#65 `_, `#66 `_, `#67 `_, `#72 `_, `#73 `_, `#74 `_, `#75 `_, `#76 `_, `#77 `_, `#79 `_, `#81 `_, `#82 `_, `#83 `_, `#84 `_, `#85 `_, `#86 `_, `#87 `_ - - -0.2.0 (2023-03-24) -================== - -Features --------- - -* Node now supports the ``clear`` method in order to clear all children. (`#23 `_) -* Constants for absolute and relative font sizing were added. (`#43 `_) - - -Bugfixes --------- - -* Handling of ``none`` as a property value has been corrected. (`#3 `_) - - -Improved Documentation ----------------------- - -* Details on towncrier and pre-commit ussage were added to the README. (`#18 `_) - - -Misc ----- - -* `#22 `_, `#24 `_, `#25 `_, `#26 `_, `#30 `_, `#34 `_, `#35 `_, `#36 `_, `#37 `_, `#38 `_, `#39 `_, `#40 `_, `#41 `_, `#42 `_ - - -0.1.3 (2020-05-25) ------------------- - -Features -^^^^^^^^ - -* Introduced some constants used by Pack that have more general uses. (`#5 `_) -* Added the ability to add, insert and remove children from a node tree. (`#10 `_) -* Added color validation in rgba and hsla constructors (`#17 `_) -* Added support for declaring a system default font size. (`#19 `_) - -Misc -^^^^ - -* `#15 `_, `#16 `_ - - -0.1.2 ------ -* Added constants for system and message fonts -* Added hash method to fonts and colors - -0.1.1 ------ - -* Added font definitions - -0.1.0 ------ - -Initial release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index ec4f1c0..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,7 +0,0 @@ -# Contributing - -BeeWare <3's contributions! - -Please be aware, BeeWare operates under a Code of Conduct. - -See [CONTRIBUTING to BeeWare](https://beeware.org/contributing) for details. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d4e6b20..0000000 --- a/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2017 Russell Keith-Magee. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of Travertino nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.rst b/README.rst index 950fc46..2395053 100644 --- a/README.rst +++ b/README.rst @@ -1,102 +1,9 @@ -.. |logo| image:: https://beeware.org/static/images/defaultlogo.png - :width: 72px - :target: https://beeware.org - -.. |pyversions| image:: https://img.shields.io/pypi/pyversions/travertino.svg - :target: https://pypi.python.org/pypi/travertino - :alt: Python Versions - -.. |version| image:: https://img.shields.io/pypi/v/travertino.svg - :target: https://pypi.python.org/pypi/travertino - :alt: Project version - -.. |maturity| image:: https://img.shields.io/pypi/status/travertino.svg - :target: https://pypi.python.org/pypi/travertino - :alt: Project status - -.. |license| image:: https://img.shields.io/pypi/l/travertino.svg - :target: https://github.com/beeware/travertino/blob/main/LICENSE - :alt: BSD License - -.. |ci| image:: https://github.com/beeware/travertino/workflows/CI/badge.svg?branch=main - :target: https://github.com/beeware/travertino/actions - :alt: Build Status - -.. |social| image:: https://img.shields.io/discord/836455665257021440?label=Discord%20Chat&logo=discord&style=plastic - :target: https://beeware.org/bee/chat/ - :alt: Discord server - -**NOTE:** This repository is archived. Current development of Travertino has moved to -the `Toga repository`_. - |logo| Travertino ========== -|pyversions| |version| |maturity| |license| |ci| |social| - -Travertino is a set of constants and utilities for describing user -interfaces, including: - -* colors -* directions -* text alignment -* sizes - -Usage ------ - -Install Travertino: - - $ pip install travertino - -Then in your python code, import and use it:: - - >>> from travertino.colors import color, rgb, - - # Define a new color as an RGB triple - >>> red = rgb(0xff, 0x00, 0x00) - - # Parse a color from a string - >>> color('#dead00') - rgb(0xde, 0xad, 0x00) - - # Reference a pre-defined color - >>> color('RebeccaPurple') - rgb(102, 51, 153) - - -Community ---------- - -Travertino is part of the `BeeWare suite`_. You can talk to the community through: - -* `@beeware@fosstodon.org on Mastodon `__ - -* `Discord `__ - -We foster a welcoming and respectful community as described in our -`BeeWare Community Code of Conduct`_. - -Contributing ------------- - -If you experience problems with Travertino, `log them on GitHub`_. If you -want to contribute code, please `fork the code`_ and `submit a pull request`_. - -Travertino uses `Pre-commit `__ and `TownCrier -`__ to help maintain code quality. For -details on how to use these tools as part of your development environment, see -the `Briefcase code contribution guide -`__. -Although that document is for a different project, the details about setting up -your development environment are the same. +This repository is no longer in use. Current development of Travertino has moved to +the `Toga repository`_. .. _Toga repository: https://github.com/beeware/toga -.. _BeeWare suite: https://beeware.org -.. _Read The Docs: https://travertino.readthedocs.io -.. _BeeWare Community Code of Conduct: https://beeware.org/community/behavior/ -.. _log them on Github: https://github.com/beeware/travertino/issues -.. _fork the code: https://github.com/beeware/travertino -.. _submit a pull request: https://github.com/beeware/travertino/pulls diff --git a/changes/.gitignore b/changes/.gitignore deleted file mode 100644 index f935021..0000000 --- a/changes/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/changes/100.misc.rst b/changes/100.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/100.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/101.misc.rst b/changes/101.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/101.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/102.misc.rst b/changes/102.misc.rst deleted file mode 100644 index 74ff099..0000000 --- a/changes/102.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.0.0 to 4.1.0. diff --git a/changes/103.misc.rst b/changes/103.misc.rst deleted file mode 100644 index 01e2e04..0000000 --- a/changes/103.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools-scm[toml] from 7.1.0 to 8.0.3. diff --git a/changes/104.misc.rst b/changes/104.misc.rst deleted file mode 100644 index 6375be3..0000000 --- a/changes/104.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pre-commit-hooks`` was updated to its latest version. diff --git a/changes/105.misc.rst b/changes/105.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/105.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/106.misc.rst b/changes/106.misc.rst deleted file mode 100644 index 8b779af..0000000 --- a/changes/106.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 4.7.0 to 4.7.1. diff --git a/changes/107.misc.rst b/changes/107.misc.rst deleted file mode 100644 index eccc8b2..0000000 --- a/changes/107.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools-scm[toml] from 8.0.3 to 8.0.4. diff --git a/changes/108.misc.rst b/changes/108.misc.rst deleted file mode 100644 index bf1cea0..0000000 --- a/changes/108.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.4.0 to 3.5.0. diff --git a/changes/109.misc.rst b/changes/109.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/109.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/110.misc.rst b/changes/110.misc.rst deleted file mode 100644 index 160e818..0000000 --- a/changes/110.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.0 to 4.1.1. diff --git a/changes/111.misc.rst b/changes/111.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/111.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/112.misc.rst b/changes/112.misc.rst deleted file mode 100644 index bb397bb..0000000 --- a/changes/112.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 7.4.2 to 7.4.3. diff --git a/changes/113.misc.rst b/changes/113.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/113.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/114.misc.rst b/changes/114.misc.rst deleted file mode 100644 index 2c53049..0000000 --- a/changes/114.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.11.3 to 4.11.4. diff --git a/changes/115.misc.rst b/changes/115.misc.rst deleted file mode 100644 index 27e57a7..0000000 --- a/changes/115.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``isort`` was updated to its latest version. diff --git a/changes/116.misc.rst b/changes/116.misc.rst deleted file mode 100644 index eb4163a..0000000 --- a/changes/116.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.5.0 to 3.6.0. diff --git a/changes/117.misc.rst b/changes/117.misc.rst deleted file mode 100644 index cb2859a..0000000 --- a/changes/117.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 4.7.1 to 5.0.0. diff --git a/changes/118.misc.rst b/changes/118.misc.rst deleted file mode 100644 index 568b5f9..0000000 --- a/changes/118.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The project was moved to a pure PEP621 configuration. diff --git a/changes/120.misc.rst b/changes/120.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/120.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/121.misc.rst b/changes/121.misc.rst deleted file mode 100644 index 27e57a7..0000000 --- a/changes/121.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``isort`` was updated to its latest version. diff --git a/changes/122.misc.rst b/changes/122.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/122.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/123.misc.rst b/changes/123.misc.rst deleted file mode 100644 index 3b24578..0000000 --- a/changes/123.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 7.4.3 to 7.4.4. diff --git a/changes/124.misc.rst b/changes/124.misc.rst deleted file mode 100644 index eeb17f0..0000000 --- a/changes/124.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``upload-artifact`` and ``download-artifact`` CI actions were upgraded to v4. diff --git a/changes/125.misc.rst b/changes/125.misc.rst deleted file mode 100644 index e1b6193..0000000 --- a/changes/125.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``flake8`` was updated to its latest version. diff --git a/changes/126.misc.rst b/changes/126.misc.rst deleted file mode 100644 index 5976996..0000000 --- a/changes/126.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.0 to 4.1.1. diff --git a/changes/127.misc.rst b/changes/127.misc.rst deleted file mode 100644 index eb7038b..0000000 --- a/changes/127.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.11.4 to 4.12.0. diff --git a/changes/128.misc.rst b/changes/128.misc.rst deleted file mode 100644 index d4b29cf..0000000 --- a/changes/128.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.12.0 to 4.12.1. diff --git a/changes/129.misc.rst b/changes/129.misc.rst deleted file mode 100644 index a30c5ee..0000000 --- a/changes/129.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 7.4.4 to 8.0.0. diff --git a/changes/130.misc.rst b/changes/130.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/130.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/131.misc.rst b/changes/131.misc.rst deleted file mode 100644 index f128cd0..0000000 --- a/changes/131.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.6.0 to 3.6.1. diff --git a/changes/132.misc.rst b/changes/132.misc.rst deleted file mode 100644 index b65b631..0000000 --- a/changes/132.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated ncipollo/release-action from 1.13.0 to 1.14.0. diff --git a/changes/133.misc.rst b/changes/133.misc.rst deleted file mode 100644 index 572b3e4..0000000 --- a/changes/133.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.1 to 4.1.2. diff --git a/changes/134.misc.rst b/changes/134.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/134.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/135.misc.rst b/changes/135.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/135.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/136.misc.rst b/changes/136.misc.rst deleted file mode 100644 index f65d53f..0000000 --- a/changes/136.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.6.1 to 3.6.2. diff --git a/changes/137.misc.rst b/changes/137.misc.rst deleted file mode 100644 index 1ec3602..0000000 --- a/changes/137.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.12.1 to 4.13.0. diff --git a/changes/138.misc.rst b/changes/138.misc.rst deleted file mode 100644 index 6685071..0000000 --- a/changes/138.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.0.0 to 8.0.1. diff --git a/changes/139.removal.rst b/changes/139.removal.rst deleted file mode 100644 index db7e079..0000000 --- a/changes/139.removal.rst +++ /dev/null @@ -1 +0,0 @@ -The 'default' parameter for Choice has been deprecated. diff --git a/changes/140.misc.rst b/changes/140.misc.rst deleted file mode 100644 index 9a00163..0000000 --- a/changes/140.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.0.1 to 8.0.2. diff --git a/changes/141.feature.rst b/changes/141.feature.rst deleted file mode 100644 index 8c88e55..0000000 --- a/changes/141.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Validated properties of styles can now be defined as dataclass class attributes. diff --git a/changes/142.misc.rst b/changes/142.misc.rst deleted file mode 100644 index 7dacb15..0000000 --- a/changes/142.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.2 to 4.1.4. diff --git a/changes/143.feature.rst b/changes/143.feature.rst deleted file mode 100644 index 234ac89..0000000 --- a/changes/143.feature.rst +++ /dev/null @@ -1 +0,0 @@ -BaseStyle now supports |, |=, and 'in' operators. diff --git a/changes/144.misc.rst b/changes/144.misc.rst deleted file mode 100644 index 5ab45a4..0000000 --- a/changes/144.misc.rst +++ /dev/null @@ -1 +0,0 @@ -A CI workflow was added to bump dependencies in configuration files. diff --git a/changes/145.misc.rst b/changes/145.misc.rst deleted file mode 100644 index 1b2f141..0000000 --- a/changes/145.misc.rst +++ /dev/null @@ -1 +0,0 @@ -All tests are now in native pytest format. diff --git a/changes/146.misc.rst b/changes/146.misc.rst deleted file mode 100644 index c724abc..0000000 --- a/changes/146.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.13.0 to 4.14.1. diff --git a/changes/147.misc.rst b/changes/147.misc.rst deleted file mode 100644 index 5291155..0000000 --- a/changes/147.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.0.2 to 8.1.1. diff --git a/changes/148.feature.rst b/changes/148.feature.rst deleted file mode 100644 index 686fd7a..0000000 --- a/changes/148.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added a ``list_property`` for storing multi-valued elements. diff --git a/changes/149.feature.rst b/changes/149.feature.rst deleted file mode 100644 index 4ddcec9..0000000 --- a/changes/149.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Support for Python 3.13 was added. diff --git a/changes/150.misc.rst b/changes/150.misc.rst deleted file mode 100644 index 69bce8d..0000000 --- a/changes/150.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.1 to 4.1.2. diff --git a/changes/151.misc.rst b/changes/151.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/151.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/152.misc.rst b/changes/152.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/152.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/154.misc.rst b/changes/154.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/154.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/155.misc.rst b/changes/155.misc.rst deleted file mode 100644 index 1121d37..0000000 --- a/changes/155.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.14.1 to 4.14.2. diff --git a/changes/156.misc.rst b/changes/156.misc.rst deleted file mode 100644 index 922f810..0000000 --- a/changes/156.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.6.2 to 3.7.0. diff --git a/changes/157.misc.rst b/changes/157.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/157.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/158.misc.rst b/changes/158.misc.rst deleted file mode 100644 index 0b1d812..0000000 --- a/changes/158.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 5.0.0 to 5.1.0. diff --git a/changes/159.misc.rst b/changes/159.misc.rst deleted file mode 100644 index 6375be3..0000000 --- a/changes/159.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pre-commit-hooks`` was updated to its latest version. diff --git a/changes/160.misc.rst b/changes/160.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/160.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/161.misc.rst b/changes/161.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/161.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/162.misc.rst b/changes/162.misc.rst deleted file mode 100644 index 305a1ff..0000000 --- a/changes/162.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.2 to 4.1.3. diff --git a/changes/163.misc.rst b/changes/163.misc.rst deleted file mode 100644 index 90f91e8..0000000 --- a/changes/163.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.4 to 4.1.5. diff --git a/changes/164.misc.rst b/changes/164.misc.rst deleted file mode 100644 index 00d09ed..0000000 --- a/changes/164.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.14.2 to 4.15.0. diff --git a/changes/165.misc.rst b/changes/165.misc.rst deleted file mode 100644 index 17658a8..0000000 --- a/changes/165.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.1.1 to 8.2.0. diff --git a/changes/166.misc.rst b/changes/166.misc.rst deleted file mode 100644 index 4b503bc..0000000 --- a/changes/166.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.3 to 4.1.4. diff --git a/changes/167.misc.rst b/changes/167.misc.rst deleted file mode 100644 index ac63d87..0000000 --- a/changes/167.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.5 to 4.1.7. diff --git a/changes/168.misc.rst b/changes/168.misc.rst deleted file mode 100644 index 24f4357..0000000 --- a/changes/168.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated dsaltares/fetch-gh-release-asset from 1.1.1 to 1.1.2. diff --git a/changes/169.misc.rst b/changes/169.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/169.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/170.doc.rst b/changes/170.doc.rst deleted file mode 100644 index 9f5196f..0000000 --- a/changes/170.doc.rst +++ /dev/null @@ -1 +0,0 @@ -The README badges were updated to display correctly on GitHub. diff --git a/changes/171.misc.rst b/changes/171.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/171.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/172.misc.rst b/changes/172.misc.rst deleted file mode 100644 index 2f50b82..0000000 --- a/changes/172.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.7.0 to 3.7.1. diff --git a/changes/173.misc.rst b/changes/173.misc.rst deleted file mode 100644 index 8edd7a8..0000000 --- a/changes/173.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools-scm from 8.0.4 to 8.1.0. diff --git a/changes/174.misc.rst b/changes/174.misc.rst deleted file mode 100644 index 37d947e..0000000 --- a/changes/174.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.4 to 4.1.5. diff --git a/changes/175.misc.rst b/changes/175.misc.rst deleted file mode 100644 index d9aa900..0000000 --- a/changes/175.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.2.0 to 8.2.1. diff --git a/changes/176.misc.rst b/changes/176.misc.rst deleted file mode 100644 index 8edd7a8..0000000 --- a/changes/176.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools-scm from 8.0.4 to 8.1.0. diff --git a/changes/177.misc.rst b/changes/177.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/177.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/178.misc.rst b/changes/178.misc.rst deleted file mode 100644 index a79d1f7..0000000 --- a/changes/178.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.5 to 4.1.6. diff --git a/changes/179.misc.rst b/changes/179.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/179.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/180.misc.rst b/changes/180.misc.rst deleted file mode 100644 index 3f31ce7..0000000 --- a/changes/180.misc.rst +++ /dev/null @@ -1 +0,0 @@ -``hynek/build-and-inspect-python-package`` is now used to create the Python package. diff --git a/changes/181.misc.rst b/changes/181.misc.rst deleted file mode 100644 index 049e78a..0000000 --- a/changes/181.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.2.1 to 8.2.2. diff --git a/changes/182.misc.rst b/changes/182.misc.rst deleted file mode 100644 index f4ec8f9..0000000 --- a/changes/182.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.15.0 to 4.15.1. diff --git a/changes/183.misc.rst b/changes/183.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/183.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/184.misc.rst b/changes/184.misc.rst deleted file mode 100644 index e1b6193..0000000 --- a/changes/184.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``flake8`` was updated to its latest version. diff --git a/changes/185.misc.rst b/changes/185.misc.rst deleted file mode 100644 index cc57469..0000000 --- a/changes/185.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.6 to 4.1.7. diff --git a/changes/186.misc.rst b/changes/186.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/186.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/187.misc.rst b/changes/187.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/187.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/188.misc.rst b/changes/188.misc.rst deleted file mode 100644 index b83bfa0..0000000 --- a/changes/188.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/download-artifact from 4.1.7 to 4.1.8. diff --git a/changes/189.misc.rst b/changes/189.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/189.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/190.misc.rst b/changes/190.misc.rst deleted file mode 100644 index bb88593..0000000 --- a/changes/190.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.15.1 to 4.16.0. diff --git a/changes/191.misc.rst b/changes/191.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/191.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/192.misc.rst b/changes/192.misc.rst deleted file mode 100644 index 8a87f60..0000000 --- a/changes/192.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 5.1.0 to 5.1.1. diff --git a/changes/193.misc.rst b/changes/193.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/193.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/194.misc.rst b/changes/194.misc.rst deleted file mode 100644 index 715ab15..0000000 --- a/changes/194.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.2.2 to 8.3.1. diff --git a/changes/195.misc.rst b/changes/195.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/195.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/196.misc.rst b/changes/196.misc.rst deleted file mode 100644 index 52a875b..0000000 --- a/changes/196.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.3.1 to 8.3.2. diff --git a/changes/197.misc.rst b/changes/197.misc.rst deleted file mode 100644 index 45f2562..0000000 --- a/changes/197.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.7.1 to 3.8.0. diff --git a/changes/199.misc.rst b/changes/199.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/199.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/200.misc.rst b/changes/200.misc.rst deleted file mode 100644 index e1b6193..0000000 --- a/changes/200.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``flake8`` was updated to its latest version. diff --git a/changes/202.misc.rst b/changes/202.misc.rst deleted file mode 100644 index ce3d0d2..0000000 --- a/changes/202.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.16.0 to 4.17.1. diff --git a/changes/204.misc.rst b/changes/204.misc.rst deleted file mode 100644 index 64a2dae..0000000 --- a/changes/204.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.17.1 to 4.18.0. diff --git a/changes/205.misc.rst b/changes/205.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/205.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/206.misc.rst b/changes/206.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/206.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/207.misc.rst b/changes/207.misc.rst deleted file mode 100644 index bf8ace3..0000000 --- a/changes/207.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 5.1.1 to 5.2.0. diff --git a/changes/208.misc.rst b/changes/208.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/208.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/209.misc.rst b/changes/209.misc.rst deleted file mode 100644 index ff9b296..0000000 --- a/changes/209.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.18.0 to 4.18.1. diff --git a/changes/210.misc.rst b/changes/210.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/210.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/211.misc.rst b/changes/211.misc.rst deleted file mode 100644 index 2048dc0..0000000 --- a/changes/211.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.3.2 to 8.3.3. diff --git a/changes/212.misc.rst b/changes/212.misc.rst deleted file mode 100644 index d112536..0000000 --- a/changes/212.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.18.1 to 4.20.0. diff --git a/changes/213.misc.rst b/changes/213.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/213.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/214.misc.rst b/changes/214.misc.rst deleted file mode 100644 index 0360df4..0000000 --- a/changes/214.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.1.7 to 4.2.0. diff --git a/changes/215.misc.rst b/changes/215.misc.rst deleted file mode 100644 index 6375be3..0000000 --- a/changes/215.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pre-commit-hooks`` was updated to its latest version. diff --git a/changes/216.misc.rst b/changes/216.misc.rst deleted file mode 100644 index 3dc996a..0000000 --- a/changes/216.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.8.0 to 4.0.0. diff --git a/changes/217.misc.rst b/changes/217.misc.rst deleted file mode 100644 index 4ac1456..0000000 --- a/changes/217.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.20.0 to 4.21.2. diff --git a/changes/218.misc.rst b/changes/218.misc.rst deleted file mode 100644 index bd8bb5d..0000000 --- a/changes/218.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black-pre-commit-mirror`` was updated to its latest version. diff --git a/changes/219.misc.rst b/changes/219.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/219.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/220.misc.rst b/changes/220.misc.rst deleted file mode 100644 index 9628a82..0000000 --- a/changes/220.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.2.0 to 4.2.1. diff --git a/changes/221.misc.rst b/changes/221.misc.rst deleted file mode 100644 index fa13cc2..0000000 --- a/changes/221.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 4.0.0 to 4.0.1. diff --git a/changes/223.feature.rst b/changes/223.feature.rst deleted file mode 100644 index 9e93932..0000000 --- a/changes/223.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Support for Python 3.14 was added. diff --git a/changes/223.removal.rst b/changes/223.removal.rst deleted file mode 100644 index 380ada5..0000000 --- a/changes/223.removal.rst +++ /dev/null @@ -1 +0,0 @@ -Python 3.8 is no longer supported. diff --git a/changes/224.bugfix.rst b/changes/224.bugfix.rst deleted file mode 100644 index 0ddcf45..0000000 --- a/changes/224.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Assigning a new style object to a node that already has an applicator assigned now properly maintains an association between the applicator and the new style, and triggers a style reapplication. diff --git a/changes/224.misc.rst b/changes/224.misc.rst deleted file mode 100644 index ef86edb..0000000 --- a/changes/224.misc.rst +++ /dev/null @@ -1 +0,0 @@ -An applicator, once assigned to a node, now receives a reference to its node (as self.node). diff --git a/changes/224.removal.1.rst b/changes/224.removal.1.rst deleted file mode 100644 index a25da88..0000000 --- a/changes/224.removal.1.rst +++ /dev/null @@ -1 +0,0 @@ -Supplying an applicator to BaseStyle.copy() has been deprecated. If you need to manually assign an applicator to a style, do it separately, after the copy. diff --git a/changes/224.removal.2.rst b/changes/224.removal.2.rst deleted file mode 100644 index 377527f..0000000 --- a/changes/224.removal.2.rst +++ /dev/null @@ -1 +0,0 @@ -The mechanisms for assigning styles and applicators to nodes, and applying styles, have been reworked. A node will now attempt to apply its style as soon as it is assigned an applicator; this means you should not assign an applicator to a node until the node is sufficiently initialized to apply its style. To accomodate uses that currently do not follow this order, any exceptions resulting from a failed style application are caught, and a runtime warning is issued. In a future version, this will be an exception. diff --git a/changes/225.misc.rst b/changes/225.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/225.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/226.misc.rst b/changes/226.misc.rst deleted file mode 100644 index f1c1e39..0000000 --- a/changes/226.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.21.2 to 4.23.0. diff --git a/changes/227.misc.rst b/changes/227.misc.rst deleted file mode 100644 index 15be23a..0000000 --- a/changes/227.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 4.2.1 to 4.2.2. diff --git a/changes/228.misc.rst b/changes/228.misc.rst deleted file mode 100644 index 8d8af9e..0000000 --- a/changes/228.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/setup-python from 5.2.0 to 5.3.0. diff --git a/changes/229.misc.rst b/changes/229.misc.rst deleted file mode 100644 index 3e25f18..0000000 --- a/changes/229.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.23.0 to 4.23.2. diff --git a/changes/230.misc.rst b/changes/230.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/230.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/231.misc.rst b/changes/231.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/231.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/232.misc.rst b/changes/232.misc.rst deleted file mode 100644 index 688215f..0000000 --- a/changes/232.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Applicator gets its reference to its node *before* the applicator is assigned to that node's style. diff --git a/changes/233.bugfix.rst b/changes/233.bugfix.rst deleted file mode 100644 index b789682..0000000 --- a/changes/233.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug which caused an equality check between a Font object and a non-Font to throw an exception instead of returning False. diff --git a/changes/234.misc.rst b/changes/234.misc.rst deleted file mode 100644 index d6d11e8..0000000 --- a/changes/234.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Removed an unnecessary reapply when assigning to a previously unassigned property, if the value being assigned is the same as the property's initial value. diff --git a/changes/235.misc.rst b/changes/235.misc.rst deleted file mode 100644 index 08cd276..0000000 --- a/changes/235.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The pinned dependencies in pyproject.toml and tox.ini were updated to their latest versions. diff --git a/changes/236.misc.rst b/changes/236.misc.rst deleted file mode 100644 index 2416f36..0000000 --- a/changes/236.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools from 75.3.0 to 75.5.0. diff --git a/changes/237.misc.rst b/changes/237.misc.rst deleted file mode 100644 index e3d9583..0000000 --- a/changes/237.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools from 75.5.0 to 75.6.0. diff --git a/changes/238.misc.rst b/changes/238.misc.rst deleted file mode 100644 index 8c2d394..0000000 --- a/changes/238.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 8.3.3 to 8.3.4. diff --git a/changes/239.misc.rst b/changes/239.misc.rst deleted file mode 100644 index 804c503..0000000 --- a/changes/239.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The order in which attributes are primed in ``Node.__init__`` has been altered, so that ``.parent`` is available when a style is applied. diff --git a/changes/240.misc.rst b/changes/240.misc.rst deleted file mode 100644 index 8eb6c87..0000000 --- a/changes/240.misc.rst +++ /dev/null @@ -1 +0,0 @@ -MicroPython compatibility diff --git a/changes/241.feature.rst b/changes/241.feature.rst deleted file mode 100644 index 4076f02..0000000 --- a/changes/241.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Constants now include START and END. diff --git a/changes/242.misc.rst b/changes/242.misc.rst deleted file mode 100644 index 9eb4606..0000000 --- a/changes/242.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/244.removal.rst b/changes/244.removal.rst deleted file mode 100644 index 033f9f9..0000000 --- a/changes/244.removal.rst +++ /dev/null @@ -1 +0,0 @@ -The API for ``Style.layout()`` has been formally specified as part of the Travertino API. The initial ``node`` argument is no longer required as part of the ``layout()`` method. A ``Style`` instance can interrogate ``self._applicator.node`` to retrieve the node to which the style is being applied. diff --git a/changes/245.misc.rst b/changes/245.misc.rst deleted file mode 100644 index 4128080..0000000 --- a/changes/245.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools from 75.6.0 to 75.7.0. diff --git a/changes/247.misc.rst b/changes/247.misc.rst deleted file mode 100644 index e8f324e..0000000 --- a/changes/247.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated setuptools from 75.7.0 to 75.8.0. diff --git a/changes/248.misc.rst b/changes/248.misc.rst deleted file mode 100644 index 594995a..0000000 --- a/changes/248.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated ncipollo/release-action from 1.14.0 to 1.15.0. diff --git a/changes/249.misc.rst b/changes/249.misc.rst deleted file mode 100644 index 8a7106c..0000000 --- a/changes/249.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The readme has been edited to reflect that Travertino development has moved to the Toga repository. diff --git a/changes/88.misc.rst b/changes/88.misc.rst deleted file mode 100644 index ea2a8e1..0000000 --- a/changes/88.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.8.0 to 4.9.0. diff --git a/changes/89.misc.rst b/changes/89.misc.rst deleted file mode 100644 index 13e36e2..0000000 --- a/changes/89.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated ncipollo/release-action from 1.12.0 to 1.13.0. diff --git a/changes/90.misc.rst b/changes/90.misc.rst deleted file mode 100644 index 53d6e44..0000000 --- a/changes/90.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 3.5.3 to 3.6.0. diff --git a/changes/91.misc.rst b/changes/91.misc.rst deleted file mode 100644 index 83d9f37..0000000 --- a/changes/91.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.9.0 to 4.10.0. diff --git a/changes/92.misc.rst b/changes/92.misc.rst deleted file mode 100644 index 1adfb8f..0000000 --- a/changes/92.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.10.0 to 4.11.1. diff --git a/changes/93.misc.rst b/changes/93.misc.rst deleted file mode 100644 index 90ce422..0000000 --- a/changes/93.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pre-commit from 3.3.3 to 3.4.0. diff --git a/changes/94.misc.rst b/changes/94.misc.rst deleted file mode 100644 index e86466d..0000000 --- a/changes/94.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 7.4.0 to 7.4.1. diff --git a/changes/95.misc.rst b/changes/95.misc.rst deleted file mode 100644 index 98fd070..0000000 --- a/changes/95.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated pytest from 7.4.1 to 7.4.2. diff --git a/changes/96.misc.rst b/changes/96.misc.rst deleted file mode 100644 index 7bd883b..0000000 --- a/changes/96.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated tox from 4.11.1 to 4.11.3. diff --git a/changes/97.misc.rst b/changes/97.misc.rst deleted file mode 100644 index 5ba9f59..0000000 --- a/changes/97.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated actions/checkout from 3.6.0 to 4.0.0. diff --git a/changes/98.misc.rst b/changes/98.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/98.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/99.misc.rst b/changes/99.misc.rst deleted file mode 100644 index 0be9300..0000000 --- a/changes/99.misc.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pre-commit`` hook for ``black`` was updated to its latest version. diff --git a/changes/template.rst b/changes/template.rst deleted file mode 100644 index 9c7243e..0000000 --- a/changes/template.rst +++ /dev/null @@ -1,32 +0,0 @@ -{% for section, _ in sections.items() %} -{% set underline = underlines[0] %}{% if section %}{{section}} -{{ underline * section|length }}{% set underline = underlines[1] %} - -{% endif %} - -{% if sections[section] %} -{% for category, val in definitions.items() if category in sections[section]%} -{{ definitions[category]['name'] }} -{{ underline * definitions[category]['name']|length }} - -{% if definitions[category]['showcontent'] %} -{% for text, values in sections[section][category].items() %} -* {{ text }} ({{ values|join(', ') }}) -{% endfor %} - -{% else %} -* {{ sections[section][category]['']|join(', ') }} - -{% endif %} -{% if sections[section][category]|length == 0 %} -No significant changes. - -{% else %} -{% endif %} - -{% endfor %} -{% else %} -No significant changes. - -{% endif %} -{% endfor %} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index f389337..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,82 +0,0 @@ -[build-system] -requires = [ - "setuptools==75.8.0", - "setuptools_scm==8.1.0", -] -build-backend = "setuptools.build_meta" - -[project] -dynamic = ["version"] -name = "travertino" -description = "A set of constants and base classes for describing user interface layouts." -readme = "README.rst" -requires-python = ">= 3.9" -license.text = "New BSD" -authors = [ - {name="Russell Keith-Magee", email="russell@keith-magee.com"}, -] -maintainers = [ - {name="BeeWare Team", email="team@beeware.org"}, -] -keywords = [ - "css", - "box model", - "layout", -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "Programming Language :: Python :: 3 :: Only", - "Topic :: Software Development", - "Topic :: Software Development :: User Interfaces", -] - -[project.optional-dependencies] -# Extras used by developers *of* Travertino are pinned to specific versions to -# ensure environment consistency. -dev = [ - "pre-commit == 4.0.1", - "pytest == 8.3.4", - "setuptools_scm == 8.1.0", - "tox == 4.23.2", -] - -[project.urls] -Homepage = "https://beeware.org/travertino" -Funding = "https://beeware.org/contributing/membership/" -# Documentation = "https://travertino.readthedocs.io/en/latest/" -Tracker = "https://github.com/beeware/travertino/issues" -Source = "https://github.com/beeware/travertino" - -[tool.isort] -profile = "black" -split_on_trailing_comma = true -combine_as_imports = true - -[tool.setuptools_scm] -# To enable SCM versioning, we need an empty tool configuration for setuptools_scm - -[tool.towncrier] -directory = "changes" -package = "travertino" -package_dir = "src" -filename = "CHANGELOG.rst" -title_format = "{version} ({project_date})" -issue_format = "`#{issue} `_" -template = "changes/template.rst" -type = [ - { directory = "feature", name = "Features", showcontent = true }, - { directory = "bugfix", name = "Bugfixes", showcontent = true }, - { directory = "removal", name = "Backward Incompatible Changes", showcontent = true }, - { directory = "doc", name = "Documentation", showcontent = true }, - { directory = "misc", name = "Misc", showcontent = false }, -] diff --git a/src/travertino/__init__.py b/src/travertino/__init__.py deleted file mode 100644 index ec61d4b..0000000 --- a/src/travertino/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -try: - # Read version from SCM metadata - # This will only exist in a development environment - from setuptools_scm import get_version - - # Excluded from coverage because a pure test environment (such as the one - # used by tox in CI) won't have setuptools_scm - __version__ = get_version("../..", relative_to=__file__) # pragma: no cover -except (ModuleNotFoundError, LookupError): - # If setuptools_scm isn't in the environment, the call to import will fail. - # If it *is* in the environment, but the code isn't a git checkout (e.g., - # it's been pip installed non-editable) the call to get_version() will fail. - # If either of these occurs, read version from the installer metadata. - - from importlib.metadata import version - - __version__ = version("travertino") diff --git a/src/travertino/colors.py b/src/travertino/colors.py deleted file mode 100644 index a6b91ee..0000000 --- a/src/travertino/colors.py +++ /dev/null @@ -1,403 +0,0 @@ -# flake8: NOQA: F405 -from .constants import * - - -class Color: - "A base class for all colorspace representations" - pass - - def __eq__(self, other): - try: - c1 = self.rgba - c2 = other.rgba - - return c1.r == c2.r and c1.g == c2.g and c1.b == c2.b and c1.a == c2.a - except AttributeError: - return False - - @classmethod - def _validate_between(cls, content_name, value, min_value, max_value): - if value < min_value or value > max_value: - raise ValueError( - "{} value should be between {}-{}. Got {}".format( - content_name, min_value, max_value, value - ) - ) - - @classmethod - def _validate_partial(cls, content_name, value): - cls._validate_between(content_name, value, 0, 1) - - @classmethod - def _validate_alpha(cls, value): - cls._validate_partial("alpha", value) - - -class rgba(Color): - "A representation of an RGBA color" - - def __init__(self, r, g, b, a): - self._validate_rgb("red", r) - self._validate_rgb("green", g) - self._validate_rgb("blue", b) - self._validate_alpha(a) - self.r = r - self.g = g - self.b = b - self.a = a - - def __hash__(self): - return hash(("RGBA-color", self.r, self.g, self.b, self.a)) - - def __repr__(self): - return f"rgba({self.r}, {self.g}, {self.b}, {self.a})" - - @classmethod - def _validate_rgb(cls, content_name, value): - cls._validate_between(content_name, value, 0, 255) - - @property - def rgba(self): - return self - - -class rgb(rgba): - "A representation of an RGB color" - - def __init__(self, r, g, b): - super().__init__(r, g, b, 1.0) - - def __repr__(self): - return f"rgb({self.r}, {self.g}, {self.b})" - - -class hsla(Color): - "A representation of an HSLA color" - - def __init__(self, h, s, l, a=1.0): - self._validate_between("hue", h, 0, 360) - self._validate_partial("saturation", s) - self._validate_partial("lightness", l) - self._validate_alpha(a) - self.h = h - self.s = s - self.l = l # NOQA; E741 - self.a = a - - def __hash__(self): - return hash(("HSLA-color", self.h, self.s, self.l, self.a)) - - def __repr__(self): - return f"hsla({self.h}, {self.s}, {self.l}, {self.a})" - - @property - def rgba(self): - c = (1.0 - abs(2.0 * self.l - 1.0)) * self.s - h = self.h / 60.0 - x = c * (1.0 - abs(h % 2 - 1.0)) - m = self.l - 0.5 * c - - if h < 1.0: - r, g, b = c + m, x + m, m - elif h < 2.0: - r, g, b = x + m, c + m, m - elif h < 3.0: - r, g, b = m, c + m, x + m - elif h < 4.0: - r, g, b = m, x + m, c + m - elif h < 5.0: - r, g, b = m, x + m, c + m - else: - r, g, b = c + m, m, x + m - - return rgba( - round(r * 0xFF), - round(g * 0xFF), - round(b * 0xFF), - self.a, - ) - - -class hsl(hsla): - "A representation of an HSL color" - - def __init__(self, h, s, l): - super().__init__(h, s, l, 1.0) - - def __repr__(self): - return f"hsl({self.h}, {self.s}, {self.l})" - - -def color(value): - """Parse a color from a value. - - Accepts: - * rgb() instances - * hsl() instances - * '#rgb' - * '#rgba' - * '#rrggbb' - * '#rrggbbaa' - * '#RGB' - * '#RGBA' - * '#RRGGBB' - * '#RRGGBBAA' - * 'rgb(0, 0, 0)' - * 'rgba(0, 0, 0, 0.0)' - * 'hsl(0, 0%, 0%)' - * 'hsla(0, 0%, 0%, 0.0)' - * A named color - """ - - if isinstance(value, Color): - return value - - elif isinstance(value, str): - if value[0] == "#": - if len(value) == 4: - return rgb( - r=int(value[1] + value[1], 16), - g=int(value[2] + value[2], 16), - b=int(value[3] + value[3], 16), - ) - elif len(value) == 5: - return rgba( - r=int(value[1] + value[1], 16), - g=int(value[2] + value[2], 16), - b=int(value[3] + value[3], 16), - a=int(value[4] + value[4], 16) / 0xFF, - ) - elif len(value) == 7: - return rgb( - r=int(value[1:3], 16), - g=int(value[3:5], 16), - b=int(value[5:7], 16), - ) - elif len(value) == 9: - return rgba( - r=int(value[1:3], 16), - g=int(value[3:5], 16), - b=int(value[5:7], 16), - a=int(value[7:9], 16) / 0xFF, - ) - elif value.startswith("rgba"): - try: - values = value[5:-1].split(",") - if len(values) == 4: - return rgba( - int(values[0]), - int(values[1]), - int(values[2]), - float( - values[3], - ), - ) - except ValueError: - pass - elif value.startswith("rgb"): - try: - values = value[4:-1].split(",") - if len(values) == 3: - return rgb( - int(values[0]), - int(values[1]), - int(values[2]), - ) - except ValueError: - pass - - elif value.startswith("hsla"): - try: - values = value[5:-1].split(",") - if len(values) == 4: - return hsla( - int(values[0]), - int(values[1].strip().rstrip("%")) / 100.0, - int(values[2].strip().rstrip("%")) / 100.0, - float(values[3]), - ) - except ValueError: - pass - - elif value.startswith("hsl"): - try: - values = value[4:-1].split(",") - if len(values) == 3: - return hsl( - int(values[0]), - int(values[1].strip().rstrip("%")) / 100.0, - int(values[2].strip().rstrip("%")) / 100.0, - ) - except ValueError: - pass - else: - try: - return NAMED_COLOR[value.lower()] - except KeyError: - pass - - raise ValueError("Unknown color %s" % value) - - -NAMED_COLOR = { - ALICEBLUE: rgb(0xF0, 0xF8, 0xFF), - ANTIQUEWHITE: rgb(0xFA, 0xEB, 0xD7), - AQUA: rgb(0x00, 0xFF, 0xFF), - AQUAMARINE: rgb(0x7F, 0xFF, 0xD4), - AZURE: rgb(0xF0, 0xFF, 0xFF), - BEIGE: rgb(0xF5, 0xF5, 0xDC), - BISQUE: rgb(0xFF, 0xE4, 0xC4), - BLACK: rgb(0x00, 0x00, 0x00), - BLANCHEDALMOND: rgb(0xFF, 0xEB, 0xCD), - BLUE: rgb(0x00, 0x00, 0xFF), - BLUEVIOLET: rgb(0x8A, 0x2B, 0xE2), - BROWN: rgb(0xA5, 0x2A, 0x2A), - BURLYWOOD: rgb(0xDE, 0xB8, 0x87), - CADETBLUE: rgb(0x5F, 0x9E, 0xA0), - CHARTREUSE: rgb(0x7F, 0xFF, 0x00), - CHOCOLATE: rgb(0xD2, 0x69, 0x1E), - CORAL: rgb(0xFF, 0x7F, 0x50), - CORNFLOWERBLUE: rgb(0x64, 0x95, 0xED), - CORNSILK: rgb(0xFF, 0xF8, 0xDC), - CRIMSON: rgb(0xDC, 0x14, 0x3C), - CYAN: rgb(0x00, 0xFF, 0xFF), - DARKBLUE: rgb(0x00, 0x00, 0x8B), - DARKCYAN: rgb(0x00, 0x8B, 0x8B), - DARKGOLDENROD: rgb(0xB8, 0x86, 0x0B), - DARKGRAY: rgb(0xA9, 0xA9, 0xA9), - DARKGREY: rgb(0xA9, 0xA9, 0xA9), - DARKGREEN: rgb(0x00, 0x64, 0x00), - DARKKHAKI: rgb(0xBD, 0xB7, 0x6B), - DARKMAGENTA: rgb(0x8B, 0x00, 0x8B), - DARKOLIVEGREEN: rgb(0x55, 0x6B, 0x2F), - DARKORANGE: rgb(0xFF, 0x8C, 0x00), - DARKORCHID: rgb(0x99, 0x32, 0xCC), - DARKRED: rgb(0x8B, 0x00, 0x00), - DARKSALMON: rgb(0xE9, 0x96, 0x7A), - DARKSEAGREEN: rgb(0x8F, 0xBC, 0x8F), - DARKSLATEBLUE: rgb(0x48, 0x3D, 0x8B), - DARKSLATEGRAY: rgb(0x2F, 0x4F, 0x4F), - DARKSLATEGREY: rgb(0x2F, 0x4F, 0x4F), - DARKTURQUOISE: rgb(0x00, 0xCE, 0xD1), - DARKVIOLET: rgb(0x94, 0x00, 0xD3), - DEEPPINK: rgb(0xFF, 0x14, 0x93), - DEEPSKYBLUE: rgb(0x00, 0xBF, 0xFF), - DIMGRAY: rgb(0x69, 0x69, 0x69), - DIMGREY: rgb(0x69, 0x69, 0x69), - DODGERBLUE: rgb(0x1E, 0x90, 0xFF), - FIREBRICK: rgb(0xB2, 0x22, 0x22), - FLORALWHITE: rgb(0xFF, 0xFA, 0xF0), - FORESTGREEN: rgb(0x22, 0x8B, 0x22), - FUCHSIA: rgb(0xFF, 0x00, 0xFF), - GAINSBORO: rgb(0xDC, 0xDC, 0xDC), - GHOSTWHITE: rgb(0xF8, 0xF8, 0xFF), - GOLD: rgb(0xFF, 0xD7, 0x00), - GOLDENROD: rgb(0xDA, 0xA5, 0x20), - GRAY: rgb(0x80, 0x80, 0x80), - GREY: rgb(0x80, 0x80, 0x80), - GREEN: rgb(0x00, 0x80, 0x00), - GREENYELLOW: rgb(0xAD, 0xFF, 0x2F), - HONEYDEW: rgb(0xF0, 0xFF, 0xF0), - HOTPINK: rgb(0xFF, 0x69, 0xB4), - INDIANRED: rgb(0xCD, 0x5C, 0x5C), - INDIGO: rgb(0x4B, 0x00, 0x82), - IVORY: rgb(0xFF, 0xFF, 0xF0), - KHAKI: rgb(0xF0, 0xE6, 0x8C), - LAVENDER: rgb(0xE6, 0xE6, 0xFA), - LAVENDERBLUSH: rgb(0xFF, 0xF0, 0xF5), - LAWNGREEN: rgb(0x7C, 0xFC, 0x00), - LEMONCHIFFON: rgb(0xFF, 0xFA, 0xCD), - LIGHTBLUE: rgb(0xAD, 0xD8, 0xE6), - LIGHTCORAL: rgb(0xF0, 0x80, 0x80), - LIGHTCYAN: rgb(0xE0, 0xFF, 0xFF), - LIGHTGOLDENRODYELLOW: rgb(0xFA, 0xFA, 0xD2), - LIGHTGRAY: rgb(0xD3, 0xD3, 0xD3), - LIGHTGREY: rgb(0xD3, 0xD3, 0xD3), - LIGHTGREEN: rgb(0x90, 0xEE, 0x90), - LIGHTPINK: rgb(0xFF, 0xB6, 0xC1), - LIGHTSALMON: rgb(0xFF, 0xA0, 0x7A), - LIGHTSEAGREEN: rgb(0x20, 0xB2, 0xAA), - LIGHTSKYBLUE: rgb(0x87, 0xCE, 0xFA), - LIGHTSLATEGRAY: rgb(0x77, 0x88, 0x99), - LIGHTSLATEGREY: rgb(0x77, 0x88, 0x99), - LIGHTSTEELBLUE: rgb(0xB0, 0xC4, 0xDE), - LIGHTYELLOW: rgb(0xFF, 0xFF, 0xE0), - LIME: rgb(0x00, 0xFF, 0x00), - LIMEGREEN: rgb(0x32, 0xCD, 0x32), - LINEN: rgb(0xFA, 0xF0, 0xE6), - MAGENTA: rgb(0xFF, 0x00, 0xFF), - MAROON: rgb(0x80, 0x00, 0x00), - MEDIUMAQUAMARINE: rgb(0x66, 0xCD, 0xAA), - MEDIUMBLUE: rgb(0x00, 0x00, 0xCD), - MEDIUMORCHID: rgb(0xBA, 0x55, 0xD3), - MEDIUMPURPLE: rgb(0x93, 0x70, 0xDB), - MEDIUMSEAGREEN: rgb(0x3C, 0xB3, 0x71), - MEDIUMSLATEBLUE: rgb(0x7B, 0x68, 0xEE), - MEDIUMSPRINGGREEN: rgb(0x00, 0xFA, 0x9A), - MEDIUMTURQUOISE: rgb(0x48, 0xD1, 0xCC), - MEDIUMVIOLETRED: rgb(0xC7, 0x15, 0x85), - MIDNIGHTBLUE: rgb(0x19, 0x19, 0x70), - MINTCREAM: rgb(0xF5, 0xFF, 0xFA), - MISTYROSE: rgb(0xFF, 0xE4, 0xE1), - MOCCASIN: rgb(0xFF, 0xE4, 0xB5), - NAVAJOWHITE: rgb(0xFF, 0xDE, 0xAD), - NAVY: rgb(0x00, 0x00, 0x80), - OLDLACE: rgb(0xFD, 0xF5, 0xE6), - OLIVE: rgb(0x80, 0x80, 0x00), - OLIVEDRAB: rgb(0x6B, 0x8E, 0x23), - ORANGE: rgb(0xFF, 0xA5, 0x00), - ORANGERED: rgb(0xFF, 0x45, 0x00), - ORCHID: rgb(0xDA, 0x70, 0xD6), - PALEGOLDENROD: rgb(0xEE, 0xE8, 0xAA), - PALEGREEN: rgb(0x98, 0xFB, 0x98), - PALETURQUOISE: rgb(0xAF, 0xEE, 0xEE), - PALEVIOLETRED: rgb(0xDB, 0x70, 0x93), - PAPAYAWHIP: rgb(0xFF, 0xEF, 0xD5), - PEACHPUFF: rgb(0xFF, 0xDA, 0xB9), - PERU: rgb(0xCD, 0x85, 0x3F), - PINK: rgb(0xFF, 0xC0, 0xCB), - PLUM: rgb(0xDD, 0xA0, 0xDD), - POWDERBLUE: rgb(0xB0, 0xE0, 0xE6), - PURPLE: rgb(0x80, 0x00, 0x80), - REBECCAPURPLE: rgb(0x66, 0x33, 0x99), - RED: rgb(0xFF, 0x00, 0x00), - ROSYBROWN: rgb(0xBC, 0x8F, 0x8F), - ROYALBLUE: rgb(0x41, 0x69, 0xE1), - SADDLEBROWN: rgb(0x8B, 0x45, 0x13), - SALMON: rgb(0xFA, 0x80, 0x72), - SANDYBROWN: rgb(0xF4, 0xA4, 0x60), - SEAGREEN: rgb(0x2E, 0x8B, 0x57), - SEASHELL: rgb(0xFF, 0xF5, 0xEE), - SIENNA: rgb(0xA0, 0x52, 0x2D), - SILVER: rgb(0xC0, 0xC0, 0xC0), - SKYBLUE: rgb(0x87, 0xCE, 0xEB), - SLATEBLUE: rgb(0x6A, 0x5A, 0xCD), - SLATEGRAY: rgb(0x70, 0x80, 0x90), - SLATEGREY: rgb(0x70, 0x80, 0x90), - SNOW: rgb(0xFF, 0xFA, 0xFA), - SPRINGGREEN: rgb(0x00, 0xFF, 0x7F), - STEELBLUE: rgb(0x46, 0x82, 0xB4), - TAN: rgb(0xD2, 0xB4, 0x8C), - TEAL: rgb(0x00, 0x80, 0x80), - THISTLE: rgb(0xD8, 0xBF, 0xD8), - TOMATO: rgb(0xFF, 0x63, 0x47), - TURQUOISE: rgb(0x40, 0xE0, 0xD0), - VIOLET: rgb(0xEE, 0x82, 0xEE), - WHEAT: rgb(0xF5, 0xDE, 0xB3), - WHITE: rgb(0xFF, 0xFF, 0xFF), - WHITESMOKE: rgb(0xF5, 0xF5, 0xF5), - YELLOW: rgb(0xFF, 0xFF, 0x00), - YELLOWGREEN: rgb(0x9A, 0xCD, 0x32), -} - - -__all__ = [ - "Color", - "rgba", - "rgb", - "hsla", - "hsl", - "color", - "NAMED_COLOR", - "TRANSPARENT", -] + [name.upper() for name in NAMED_COLOR.keys()] diff --git a/src/travertino/constants.py b/src/travertino/constants.py deleted file mode 100644 index 5460c39..0000000 --- a/src/travertino/constants.py +++ /dev/null @@ -1,264 +0,0 @@ -###################################################################### -# Common constants -###################################################################### - -NORMAL = "normal" -LEFT = "left" -RIGHT = "right" -TOP = "top" -BOTTOM = "bottom" -CENTER = "center" -START = "start" -END = "end" - -###################################################################### -# Direction -###################################################################### - -ROW = "row" -COLUMN = "column" - -###################################################################### -# Visibility -###################################################################### - -VISIBLE = "visible" -HIDDEN = "hidden" -NONE = "none" - -###################################################################### -# Text Justification -###################################################################### - -JUSTIFY = "justify" - -###################################################################### -# Text Direction -###################################################################### - -RTL = "rtl" -LTR = "ltr" - -###################################################################### -# Font family -###################################################################### - -SYSTEM = "system" -MESSAGE = "message" - -SERIF = "serif" -SANS_SERIF = "sans-serif" -CURSIVE = "cursive" -FANTASY = "fantasy" -MONOSPACE = "monospace" - -###################################################################### -# Font Styling -###################################################################### - -ITALIC = "italic" -OBLIQUE = "oblique" - -FONT_STYLES = {ITALIC, OBLIQUE} - -###################################################################### -# Font Variant -###################################################################### - -SMALL_CAPS = "small-caps" - -FONT_VARIANTS = {SMALL_CAPS} - -###################################################################### -# Font boldness -###################################################################### - -BOLD = "bold" - -FONT_WEIGHTS = {BOLD} - -###################################################################### -# Font Size -###################################################################### - -SYSTEM_DEFAULT_FONT_SIZE = -1 - -XX_SMALL = "xx-small" -X_SMALL = "x-small" -SMALL = "small" -MEDIUM = "medium" -LARGE = "large" -X_LARGE = "x-large" -XX_LARGE = "xx-large" -XXX_LARGE = "xxx-large" - -ABSOLUTE_FONT_SIZES = { - XX_SMALL, - X_SMALL, - SMALL, - MEDIUM, - LARGE, - X_LARGE, - XX_LARGE, - XXX_LARGE, -} - -LARGER = "larger" -SMALLER = "smaller" - -RELATIVE_FONT_SIZES = {LARGER, SMALLER} - -###################################################################### -# Colors -###################################################################### - -TRANSPARENT = "transparent" - -ALICEBLUE = "aliceblue" -ANTIQUEWHITE = "antiquewhite" -AQUA = "aqua" -AQUAMARINE = "aquamarine" -AZURE = "azure" -BEIGE = "beige" -BISQUE = "bisque" -BLACK = "black" -BLANCHEDALMOND = "blanchedalmond" -BLUE = "blue" -BLUEVIOLET = "blueviolet" -BROWN = "brown" -BURLYWOOD = "burlywood" -CADETBLUE = "cadetblue" -CHARTREUSE = "chartreuse" -CHOCOLATE = "chocolate" -CORAL = "coral" -CORNFLOWERBLUE = "cornflowerblue" -CORNSILK = "cornsilk" -CRIMSON = "crimson" -CYAN = "cyan" -DARKBLUE = "darkblue" -DARKCYAN = "darkcyan" -DARKGOLDENROD = "darkgoldenrod" -DARKGRAY = "darkgray" -DARKGREY = "darkgrey" -DARKGREEN = "darkgreen" -DARKKHAKI = "darkkhaki" -DARKMAGENTA = "darkmagenta" -DARKOLIVEGREEN = "darkolivegreen" -DARKORANGE = "darkorange" -DARKORCHID = "darkorchid" -DARKRED = "darkred" -DARKSALMON = "darksalmon" -DARKSEAGREEN = "darkseagreen" -DARKSLATEBLUE = "darkslateblue" -DARKSLATEGRAY = "darkslategray" -DARKSLATEGREY = "darkslategrey" -DARKTURQUOISE = "darkturquoise" -DARKVIOLET = "darkviolet" -DEEPPINK = "deeppink" -DEEPSKYBLUE = "deepskyblue" -DIMGRAY = "dimgray" -DIMGREY = "dimgrey" -DODGERBLUE = "dodgerblue" -FIREBRICK = "firebrick" -FLORALWHITE = "floralwhite" -FORESTGREEN = "forestgreen" -FUCHSIA = "fuchsia" -GAINSBORO = "gainsboro" -GHOSTWHITE = "ghostwhite" -GOLD = "gold" -GOLDENROD = "goldenrod" -GRAY = "gray" -GREY = "grey" -GREEN = "green" -GREENYELLOW = "greenyellow" -HONEYDEW = "honeydew" -HOTPINK = "hotpink" -INDIANRED = "indianred" -INDIGO = "indigo" -IVORY = "ivory" -KHAKI = "khaki" -LAVENDER = "lavender" -LAVENDERBLUSH = "lavenderblush" -LAWNGREEN = "lawngreen" -LEMONCHIFFON = "lemonchiffon" -LIGHTBLUE = "lightblue" -LIGHTCORAL = "lightcoral" -LIGHTCYAN = "lightcyan" -LIGHTGOLDENRODYELLOW = "lightgoldenrodyellow" -LIGHTGRAY = "lightgray" -LIGHTGREY = "lightgrey" -LIGHTGREEN = "lightgreen" -LIGHTPINK = "lightpink" -LIGHTSALMON = "lightsalmon" -LIGHTSEAGREEN = "lightseagreen" -LIGHTSKYBLUE = "lightskyblue" -LIGHTSLATEGRAY = "lightslategray" -LIGHTSLATEGREY = "lightslategrey" -LIGHTSTEELBLUE = "lightsteelblue" -LIGHTYELLOW = "lightyellow" -LIME = "lime" -LIMEGREEN = "limegreen" -LINEN = "linen" -MAGENTA = "magenta" -MAROON = "maroon" -MEDIUMAQUAMARINE = "mediumaquamarine" -MEDIUMBLUE = "mediumblue" -MEDIUMORCHID = "mediumorchid" -MEDIUMPURPLE = "mediumpurple" -MEDIUMSEAGREEN = "mediumseagreen" -MEDIUMSLATEBLUE = "mediumslateblue" -MEDIUMSPRINGGREEN = "mediumspringgreen" -MEDIUMTURQUOISE = "mediumturquoise" -MEDIUMVIOLETRED = "mediumvioletred" -MIDNIGHTBLUE = "midnightblue" -MINTCREAM = "mintcream" -MISTYROSE = "mistyrose" -MOCCASIN = "moccasin" -NAVAJOWHITE = "navajowhite" -NAVY = "navy" -OLDLACE = "oldlace" -OLIVE = "olive" -OLIVEDRAB = "olivedrab" -ORANGE = "orange" -ORANGERED = "orangered" -ORCHID = "orchid" -PALEGOLDENROD = "palegoldenrod" -PALEGREEN = "palegreen" -PALETURQUOISE = "paleturquoise" -PALEVIOLETRED = "palevioletred" -PAPAYAWHIP = "papayawhip" -PEACHPUFF = "peachpuff" -PERU = "peru" -PINK = "pink" -PLUM = "plum" -POWDERBLUE = "powderblue" -PURPLE = "purple" -REBECCAPURPLE = "rebeccapurple" -RED = "red" -ROSYBROWN = "rosybrown" -ROYALBLUE = "royalblue" -SADDLEBROWN = "saddlebrown" -SALMON = "salmon" -SANDYBROWN = "sandybrown" -SEAGREEN = "seagreen" -SEASHELL = "seashell" -SIENNA = "sienna" -SILVER = "silver" -SKYBLUE = "skyblue" -SLATEBLUE = "slateblue" -SLATEGRAY = "slategray" -SLATEGREY = "slategrey" -SNOW = "snow" -SPRINGGREEN = "springgreen" -STEELBLUE = "steelblue" -TAN = "tan" -TEAL = "teal" -THISTLE = "thistle" -TOMATO = "tomato" -TURQUOISE = "turquoise" -VIOLET = "violet" -WHEAT = "wheat" -WHITE = "white" -WHITESMOKE = "whitesmoke" -YELLOW = "yellow" -YELLOWGREEN = "yellowgreen" diff --git a/src/travertino/declaration.py b/src/travertino/declaration.py deleted file mode 100644 index 54c8c14..0000000 --- a/src/travertino/declaration.py +++ /dev/null @@ -1,457 +0,0 @@ -from collections import defaultdict -from collections.abc import Mapping, Sequence -from warnings import filterwarnings, warn - -from .colors import color -from .constants import BOTTOM, LEFT, RIGHT, TOP - -# Make sure deprecation warnings are shown by default -filterwarnings("default", category=DeprecationWarning) - - -class ImmutableList: - def __init__(self, iterable): - self._data = list(iterable) - - def __getitem__(self, index): - return self._data[index] - - def __len__(self): - return len(self._data) - - def __iter__(self): - return iter(self._data) - - def __eq__(self, other): - return self._data == other - - def __str__(self): - return str(self._data) - - def __repr__(self): - return repr(self._data) - - -class Choices: - "A class to define allowable data types for a property" - - def __init__( - self, - *constants, - default=None, # DEPRECATED - string=False, - integer=False, - number=False, - color=False, - ): - if default is not None: - warn( - "The `default` argument to Choices.__init__ is deprecated. " - "Providing no initial value to a property using it is sufficient.", - DeprecationWarning, - stacklevel=2, - ) - - self.constants = set(constants) - - self.string = string - self.integer = integer - self.number = number - self.color = color - - self._options = sorted(str(c).lower().replace("_", "-") for c in self.constants) - if self.string: - self._options.append("") - if self.integer: - self._options.append("") - if self.number: - self._options.append("") - if self.color: - self._options.append("") - - def validate(self, value): - if self.string: - try: - return value.strip() - except AttributeError: - pass - if self.integer: - try: - return int(value) - except (ValueError, TypeError): - pass - if self.number: - try: - return float(value) - except (ValueError, TypeError): - pass - if self.color: - try: - return color(value) - except ValueError: - pass - for const in self.constants: - if value == const: - return const - - raise ValueError(f"{value!r} is not a valid value") - - def __str__(self): - return ", ".join(self._options) - - -class validated_property: - def __init__(self, choices, initial=None): - """Define a simple validated property attribute. - - :param choices: The available choices. - :param initial: The initial value for the property. - """ - self.choices = choices - self.initial = None - - try: - # If an initial value has been provided, it must be consistent with - # the choices specified. - if initial is not None: - self.initial = self.validate(initial) - except ValueError: - # Unfortunately, __set_name__ hasn't been called yet, so we don't know the - # property's name. - raise ValueError( - f"Invalid initial value {initial!r}. Available choices: {choices}" - ) - - def __set_name__(self, owner, name): - self.name = name - owner._BASE_PROPERTIES[owner].add(name) - owner._BASE_ALL_PROPERTIES[owner].add(name) - - def __get__(self, obj, objtype=None): - if obj is None: - return self - - value = getattr(obj, f"_{self.name}", None) - return self.initial if value is None else value - - def __set__(self, obj, value): - if value is self: - # This happens during autogenerated dataclass __init__ when no value is - # supplied. - return - - if value is None: - raise ValueError( - "Python `None` cannot be used as a style value; " - f"to reset a property, use del `style.{self.name}`." - ) - - value = self.validate(value) - - if value != getattr(obj, f"_{self.name}", self.initial): - setattr(obj, f"_{self.name}", value) - obj.apply(self.name, value) - - def __delete__(self, obj): - try: - delattr(obj, f"_{self.name}") - except AttributeError: - pass - else: - obj.apply(self.name, self.initial) - - @property - def _name_if_set(self, default=""): - return f" {self.name}" if hasattr(self, "name") else default - - def validate(self, value): - try: - return self.choices.validate(value) - except ValueError: - raise ValueError( - f"Invalid value {value!r} for property{self._name_if_set}; " - f"Valid values are: {self.choices}" - ) - - def is_set_on(self, obj): - return hasattr(obj, f"_{self.name}") - - -class list_property(validated_property): - def validate(self, value): - if isinstance(value, str): - value = [value] - elif not isinstance(value, Sequence): - raise TypeError( - f"Value for list property{self._name_if_set} must be a sequence." - ) - - if not value: - name = getattr(self, "name", "prop_name") - raise ValueError( - "List properties cannot be set to an empty sequence; " - f"to reset a property, use del `style.{name}`." - ) - - # This could be a comprehension, but then the error couldn't specify which value - # is at fault. - result = [] - for item in value: - try: - item = self.choices.validate(item) - except ValueError: - raise ValueError( - f"Invalid item value {item!r} for list property{self._name_if_set}; " - f"Valid values are: {self.choices}" - ) - result.append(item) - - return ImmutableList(result) - - -class directional_property: - DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT] - ASSIGNMENT_SCHEMES = { - # T R B L - 1: [0, 0, 0, 0], - 2: [0, 1, 0, 1], - 3: [0, 1, 2, 1], - 4: [0, 1, 2, 3], - } - - def __init__(self, name_format): - """Define a property attribute that proxies for top/right/bottom/left alternatives. - - :param name_format: The format from which to generate subproperties. "{}" will - be replaced with "_top", etc. - """ - self.name_format = name_format - - def __set_name__(self, owner, name): - self.name = name - owner._BASE_ALL_PROPERTIES[owner].add(self.name) - - def format(self, direction): - return self.name_format.format(f"_{direction}") - - def __get__(self, obj, objtype=None): - if obj is None: - return self - - return tuple(obj[self.format(direction)] for direction in self.DIRECTIONS) - - def __set__(self, obj, value): - if value is self: - # This happens during autogenerated dataclass __init__ when no value is - # supplied. - return - - if not isinstance(value, tuple): - value = (value,) - - if order := self.ASSIGNMENT_SCHEMES.get(len(value)): - for direction, index in zip(self.DIRECTIONS, order): - obj[self.format(direction)] = value[index] - else: - raise ValueError( - f"Invalid value for '{self.name}'; value must be a number, or a 1-4 tuple." - ) - - def __delete__(self, obj): - for direction in self.DIRECTIONS: - del obj[self.format(direction)] - - def is_set_on(self, obj): - return any( - hasattr(obj, self.format(direction)) for direction in self.DIRECTIONS - ) - - -class BaseStyle: - """A base class for style declarations. - - Exposes a dict-like interface. Designed for subclasses to be decorated - with @dataclass(kw_only=True), which most IDEs should be able to interpret and - provide autocompletion of argument names. On Python < 3.10, init=False can be used - to still get the keyword-only behavior from the included __init__. - """ - - _BASE_PROPERTIES = defaultdict(set) - _BASE_ALL_PROPERTIES = defaultdict(set) - - def __init_subclass__(cls): - # Give the subclass a direct reference to its properties. - cls._PROPERTIES = cls._BASE_PROPERTIES[cls] - cls._ALL_PROPERTIES = cls._BASE_ALL_PROPERTIES[cls] - - # Fallback in case subclass isn't decorated as subclass (probably from using - # previous API) or for pre-3.10, before kw_only argument existed. - def __init__(self, **style): - self.update(**style) - - @property - def _applicator(self): - return getattr(self, "_assigned_applicator", None) - - @_applicator.setter - def _applicator(self, value): - self._assigned_applicator = value - - if value is not None: - try: - self.reapply() - # This is backwards compatibility for Toga, which (at least as of - # 0.4.8), assigns style and applicator before the widget's - # implementation is available. - except Exception: - warn( - "Failed to apply style when assigning applicator, or when " - "assigning a new style once applicator is present. Node should be " - "sufficiently initialized to apply its style before it is assigned " - "an applicator. This will be an exception in a future version.", - RuntimeWarning, - stacklevel=2, - ) - - ###################################################################### - # Interface that style declarations must define - ###################################################################### - - def apply(self, property, value): - raise NotImplementedError( - "Style must define an apply method" - ) # pragma: no cover - - def layout(self, viewport): - raise NotImplementedError( - "Style must define a layout method" - ) # pragma: no cover - - ###################################################################### - # Provide a dict-like interface - ###################################################################### - - def reapply(self): - for name in self._PROPERTIES: - self.apply(name, self[name]) - - def update(self, **styles): - "Set multiple styles on the style definition." - for name, value in styles.items(): - name = name.replace("-", "_") - if name not in self._ALL_PROPERTIES: - raise NameError(f"Unknown style {name}") - - self[name] = value - - def copy(self, applicator=None): - "Create a duplicate of this style declaration." - dup = self.__class__() - dup.update(**self) - - if applicator is not None: - warn( - "Providing an applicator to BaseStyle.copy() is deprecated. Set " - "applicator afterward on the returned copy.", - DeprecationWarning, - stacklevel=2, - ) - dup._applicator = applicator - - return dup - - def __getitem__(self, name): - name = name.replace("-", "_") - if name in self._ALL_PROPERTIES: - return getattr(self, name) - raise KeyError(name) - - def __setitem__(self, name, value): - name = name.replace("-", "_") - if name in self._ALL_PROPERTIES: - setattr(self, name, value) - else: - raise KeyError(name) - - def __delitem__(self, name): - name = name.replace("-", "_") - if name in self._ALL_PROPERTIES: - delattr(self, name) - else: - raise KeyError(name) - - def keys(self): - return {name for name in self._PROPERTIES if name in self} - - def items(self): - return [(name, self[name]) for name in self._PROPERTIES if name in self] - - def __len__(self): - return sum(1 for name in self._PROPERTIES if name in self) - - def __contains__(self, name): - return name in self._ALL_PROPERTIES and ( - getattr(self.__class__, name).is_set_on(self) - ) - - def __iter__(self): - yield from (name for name in self._PROPERTIES if name in self) - - def __or__(self, other): - if isinstance(other, BaseStyle): - if self.__class__ is not other.__class__: - return NotImplemented - elif not isinstance(other, Mapping): - return NotImplemented - - result = self.copy() - result.update(**other) - return result - - def __ior__(self, other): - if isinstance(other, BaseStyle): - if self.__class__ is not other.__class__: - return NotImplemented - elif not isinstance(other, Mapping): - return NotImplemented - - self.update(**other) - return self - - ###################################################################### - # Get the rendered form of the style declaration - ###################################################################### - def __str__(self): - return "; ".join( - f"{name.replace('_', '-')}: {value}" for name, value in sorted(self.items()) - ) - - ###################################################################### - # Backwards compatibility - ###################################################################### - - @classmethod - def validated_property(cls, name, choices, initial=None): - warn( - "Defining style properties with class methods is deprecated; use class " - "attributes instead.", - DeprecationWarning, - stacklevel=2, - ) - prop = validated_property(choices, initial) - setattr(cls, name, prop) - prop.__set_name__(cls, name) - - @classmethod - def directional_property(cls, name): - warn( - "Defining style properties with class methods is deprecated; use class " - "attributes instead.", - DeprecationWarning, - stacklevel=2, - ) - name_format = name % "{}" - name = name_format.format("") - prop = directional_property(name_format) - setattr(cls, name, prop) - prop.__set_name__(cls, name) diff --git a/src/travertino/fonts.py b/src/travertino/fonts.py deleted file mode 100644 index a707f88..0000000 --- a/src/travertino/fonts.py +++ /dev/null @@ -1,199 +0,0 @@ -from .constants import ( - BOLD, - FONT_STYLES, - FONT_VARIANTS, - FONT_WEIGHTS, - ITALIC, - NORMAL, - OBLIQUE, - SMALL_CAPS, - SYSTEM_DEFAULT_FONT_SIZE, -) - - -class Font: - def __init__(self, family, size, style=NORMAL, variant=NORMAL, weight=NORMAL): - if (family[0] == "'" and family[-1] == "'") or ( - family[0] == '"' and family[-1] == '"' - ): - self.family = family[1:-1] - else: - self.family = family - - try: - self.size = int(size) - except ValueError: - try: - if size.strip().endswith("pt"): - self.size = int(size[:-2]) - else: - raise ValueError(f"Invalid font size {size!r}") - except Exception: - raise ValueError(f"Invalid font size {size!r}") - self.style = style if style in FONT_STYLES else NORMAL - self.variant = variant if variant in FONT_VARIANTS else NORMAL - self.weight = weight if weight in FONT_WEIGHTS else NORMAL - - def __hash__(self): - return hash( - ("FONT", self.family, self.size, self.style, self.variant, self.weight) - ) - - def __repr__(self): - return "".format( - "" if self.style is NORMAL else (self.style + " "), - "" if self.variant is NORMAL else (self.variant + " "), - "" if self.weight is NORMAL else (self.weight + " "), - ( - "system default size" - if self.size == SYSTEM_DEFAULT_FONT_SIZE - else f"{self.size}pt" - ), - self.family, - ) - - def __eq__(self, other): - try: - return ( - self.family == other.family - and self.size == other.size - and self.style == other.style - and self.variant == other.variant - and self.weight == other.weight - ) - except AttributeError: - return False - - def normal_style(self): - "Generate a normal style version of this font" - return Font( - self.family, - self.size, - style=NORMAL, - variant=self.variant, - weight=self.weight, - ) - - def italic(self): - "Generate an italic version of this font" - return Font( - self.family, - self.size, - style=ITALIC, - variant=self.variant, - weight=self.weight, - ) - - def oblique(self): - "Generate an oblique version of this font" - return Font( - self.family, - self.size, - style=OBLIQUE, - variant=self.variant, - weight=self.weight, - ) - - def normal_variant(self): - "Generate a normal variant of this font" - return Font( - self.family, self.size, style=self.style, variant=NORMAL, weight=self.weight - ) - - def small_caps(self): - "Generate a small-caps variant of this font" - return Font( - self.family, - self.size, - style=self.style, - variant=SMALL_CAPS, - weight=self.weight, - ) - - def normal_weight(self): - "Generate a normal weight version of this font" - return Font( - self.family, - self.size, - style=self.style, - variant=self.variant, - weight=NORMAL, - ) - - def bold(self): - "Generate a bold version of this font" - return Font( - self.family, self.size, style=self.style, variant=self.variant, weight=BOLD - ) - - -def font(value): - """Parse a font from a string. - - Accepts: - * Font instances - - style: normal / italic / oblique - variant: normal / small-caps - weight: normal / bold - - style variant weight size family - variant weight size family - weight size family - size family - """ - - if isinstance(value, Font): - return value - - elif isinstance(value, str): - parts = value.split(" ") - - style = None - variant = None - weight = None - size = None - - while size is None: - part = parts.pop(0) - if part == NORMAL: - if style is None: - style = NORMAL - elif variant is None: - variant = NORMAL - elif weight is None: - weight = NORMAL - elif part in FONT_STYLES: - if style is not None: - raise ValueError(f"Invalid font declaration '{value}'") - style = part - elif part in FONT_VARIANTS: - if variant is not None: - raise ValueError(f"Invalid font declaration '{value}'") - if style is None: - style = NORMAL - variant = part - elif part in FONT_WEIGHTS: - if weight is not None: - raise ValueError(f"Invalid font declaration '{value}'") - if style is None: - style = NORMAL - if variant is None: - variant = NORMAL - weight = part - else: - try: - if part.endswith("pt"): - size = int(part[:-2]) - else: - size = int(part) - except ValueError: - raise ValueError(f"Invalid size in font declaration '{value}'") - - if parts[0] == "pt": - parts.pop(0) - - family = " ".join(parts) - return Font(family, size, style=style, variant=variant, weight=weight) - - raise ValueError("Unknown font '%s'" % value) diff --git a/src/travertino/layout.py b/src/travertino/layout.py deleted file mode 100644 index 87e5d3c..0000000 --- a/src/travertino/layout.py +++ /dev/null @@ -1,170 +0,0 @@ -class Viewport: - """ - A viewport is a description of surface onto which content will be - rendered. It stores the size of the surface(in pixels), plus the - pixel density of the viewport. - """ - - def __init__(self, width=0, height=0, dpi=None): - self.width = width - self.height = height - self.dpi = dpi - - -class BaseBox: - """Describe the layout of a box displaying a node. - - Stored properties - ~~~~~~~~~~~~~~~~~ - visible: The node is included in rendering, and is visible. A value of - False indicates the node takes up space, but is not rendered. - - content_width: The width of the content in the box - content_height: The height of the content in the box - content_top: The top position of the content in the box, relative to the box - content_left: The left position of the content in the box, relative to the box - content_bottom: The distance from the bottom of the content to the bottom of the box - content_right: The distance from the right of the content to the right of the box - - origin_top: The absolute position of the top of the box - origin_left: The absolute position of the left of the box - - Computed properties - ~~~~~~~~~~~~~~~~~~~ - width: The overall width of the box - height: The overall height of the box - - absolute_content_top: The absolute position of the top of the content box. - absolute_content_left: The absolute position of the left of the content box. - absolute_content_bottom: The absolute position of the bottom of the content box. - absolute_content_right: The absolute position of the right of the content box. - - """ - - def __init__(self, node): - self.node = node - self._reset() - - def __repr__(self): - return "<{} ({}x{} @ {},{})>".format( - self.__class__.__name__, - self.content_width, - self.content_height, - self.absolute_content_left, - self.absolute_content_top, - ) - - def _reset(self): - # Some properties describing whether this node exists in - # layout *at all*. - self.visible = True - - # Minimum width and height of the content box. - self.min_content_width = 0 - self.min_content_height = 0 - - # Width and height of the content box. - self.content_width = 0 - self.content_height = 0 - - # Box position, relative to the containing box - self._content_top = 0 - self._content_left = 0 - self.content_bottom = 0 - self.content_right = 0 - - self.__origin_top = 0 - self.__origin_left = 0 - - # Set the origin via properties; this forces the calculation of - # absolute positions. - self._origin_top = 0 - self._origin_left = 0 - - ###################################################################### - # Origin handling - ###################################################################### - @property - def _origin_top(self): - return self.__origin_top - - @_origin_top.setter - def _origin_top(self, value): - if value != self.__origin_top: - self.__origin_top = value - for child in self.node.children: - if child.layout: - child.layout._origin_top = self.absolute_content_top - - @property - def _origin_left(self): - return self.__origin_left - - @_origin_left.setter - def _origin_left(self, value): - if value != self.__origin_left: - self.__origin_left = value - for child in self.node.children: - if child.layout: - child.layout._origin_left = self.absolute_content_left - - @property - def width(self): - return self._content_left + self.content_width + self.content_right - - @property - def min_width(self): - return self._content_left + self.min_content_width + self.content_right - - @property - def height(self): - return self._content_top + self.content_height + self.content_bottom - - @property - def min_height(self): - return self._content_top + self.min_content_height + self.content_bottom - - ###################################################################### - # Content box properties - ###################################################################### - @property - def content_top(self): - return self._content_top - - @content_top.setter - def content_top(self, value): - self._content_top = value - for child in self.node.children: - if child.layout: - child.layout._origin_top = self.absolute_content_top - - @property - def content_left(self): - return self._content_left - - @content_left.setter - def content_left(self, value): - self._content_left = value - for child in self.node.children: - if child.layout: - child.layout._origin_left = self.absolute_content_left - - ###################################################################### - # Absolute content box position - ###################################################################### - - @property - def absolute_content_top(self): - return self.__origin_top + self._content_top - - @property - def absolute_content_right(self): - return self.__origin_left + self._content_left + self.content_width - - @property - def absolute_content_bottom(self): - return self.__origin_top + self._content_top + self.content_height - - @property - def absolute_content_left(self): - return self.__origin_left + self._content_left diff --git a/src/travertino/node.py b/src/travertino/node.py deleted file mode 100644 index d95cee6..0000000 --- a/src/travertino/node.py +++ /dev/null @@ -1,196 +0,0 @@ -class Node: - def __init__(self, style, applicator=None, children=None): - # Parent needs to be primed before style is (potentially) applied with - # assignment of applicator. - self._parent = None - self._root = None - - # Explicitly set the internal attribute first, since the setter for style will - # access the applicator property. - self._applicator = None - - self.style = style - self.applicator = applicator - - if children is None: - self._children = None - else: - self._children = [] - for child in children: - self.add(child) - - @property - def style(self): - """The node's style. - - Assigning a style triggers an application of that style if an applicator has - already been assigned. - """ - return self._style - - @style.setter - def style(self, style): - self._style = style.copy() - self.intrinsic = self.style.IntrinsicSize() - self.layout = self.style.Box(self) - - if self.applicator: - self.style._applicator = self.applicator - - @property - def applicator(self): - """This node's applicator, which handles applying the style. - - Assigning an applicator triggers an application of the node's style. - """ - return self._applicator - - @applicator.setter - def applicator(self, applicator): - if self.applicator: - # If an existing applicator is present, clear its reference to this node. - self.applicator.node = None - - if applicator: - # This needs to happen *before* assigning the applicator to the style, - # below, because as part of receiving the applicator, the style will - # reapply itself. How this happens will vary with applicator - # implementation, but will probably need access to the node. - applicator.node = self - - self._applicator = applicator - # This triggers style.reapply(): - self.style._applicator = applicator - - @property - def root(self): - """The root of the tree containing this node. - - Returns: - The root node. Returns self if this node *is* the root node. - """ - return self._root if self._root else self - - @property - def parent(self): - """The parent of this node. - - Returns: - The parent of this node. Returns None if this node is the root node. - """ - return self._parent - - @property - def children(self): - """The children of this node. - This *always* returns a list, even if the node is a leaf - and cannot have children. - - Returns: - A list of the children for this widget. - """ - if self._children is None: - return [] - else: - return self._children - - @property - def can_have_children(self): - """Determine if the node can have children. - - This does not resolve whether there actually *are* any children; - it only confirms whether children are theoretically allowed. - """ - return self._children is not None - - def add(self, child): - """Add a node as a child of this one. - Args: - child: A node to add as a child to this node. - - Raises: - ValueError: If this node is a leaf, and cannot have children. - """ - if self._children is None: - raise ValueError("Cannot add children") - - self._children.append(child) - child._parent = self - self._set_root(child, self.root) - - def insert(self, index, child): - """Insert a node as a child of this one. - Args: - index: Index of child position. - child: A node to insert as a child to this node. - - Raises: - ValueError: If this node is a leaf, and cannot have children. - """ - if self._children is None: - raise ValueError("Cannot insert child") - - self._children.insert(index, child) - child._parent = self - self._set_root(child, self.root) - - def remove(self, child): - """Remove child from this node. - Args: - child: The child to remove from this node. - - Raises: - ValueError: If this node is a leaf, and cannot have children. - """ - if self._children is None: - raise ValueError("Cannot remove children") - - self._children.remove(child) - child._parent = None - self._set_root(child, None) - - def clear(self): - """Clear all children from this node. - - Raises: - ValueError: If this node is a leaf, and cannot have children. - """ - if self._children is None: - # This is a leaf, so do nothing. - return - - for child in self._children: - child._parent = None - self._set_root(child, None) - self._children = [] - - def refresh(self, viewport): - """Refresh the layout and appearance of the tree this node is contained in.""" - if self._root: - self._root.refresh(viewport) - else: - if self.applicator: - - ###################################################################### - # 2024-12: Backwards compatibility for Toga <= 0.4.8 - ###################################################################### - # Accommodate the earlier signature of layout(), which included the node - # as a parameter. - try: - self.style.layout(viewport) - except TypeError as error: - if "layout() missing 1 required positional argument:" in str(error): - self.style.layout(self, viewport) - else: - raise - ###################################################################### - # End backwards compatibility - ###################################################################### - - self.applicator.set_bounds() - - def _set_root(self, node, root): - # Propagate a root node change through a tree. - node._root = root - for child in node.children: - self._set_root(child, root) diff --git a/src/travertino/size.py b/src/travertino/size.py deleted file mode 100644 index 1104386..0000000 --- a/src/travertino/size.py +++ /dev/null @@ -1,68 +0,0 @@ -class at_least: - "An annotation to wrap around a value to describe that it is a minimum bound" - - def __init__(self, value): - self.value = value - - def __repr__(self): - return f"at least {self.value}" - - def __eq__(self, other): - try: - return self.value == other.value - except AttributeError: - return False - - -class BaseIntrinsicSize: - """Representation of the intrinsic size of an object. - - width: The width of the node. - height: The height of the node. - ratio: The height between height and width. width = height * ratio - """ - - def __init__(self, width=None, height=None, ratio=None, layout=None): - self._layout = layout - self._width = width - self._height = height - - self._ratio = None - - def __repr__(self): - return f"({self.width}, {self.height})" - - @property - def width(self): - return self._width - - @width.setter - def width(self, value): - if self._width != value: - self._width = value - - if self._layout: - self._layout.dirty(intrinsic_width=value) - - @property - def height(self): - return self._height - - @height.setter - def height(self, value): - if self._height != value: - self._height = value - - if self._layout: - self._layout.dirty(intrinsic_height=value) - - @property - def ratio(self): - return self._ratio - - @ratio.setter - def ratio(self, value): - if self._ratio != value: - self._ratio = value - if self._layout: - self._layout.dirty(intrinsic_ratio=value) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/colors/__init__.py b/tests/colors/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/colors/test_constructor.py b/tests/colors/test_constructor.py deleted file mode 100644 index 7b5ea1e..0000000 --- a/tests/colors/test_constructor.py +++ /dev/null @@ -1,170 +0,0 @@ -import pytest - -from travertino.colors import hsl, hsla, rgb, rgba - - -def assert_equal_color(actual, expected): - assert actual.rgba.r == expected.rgba.r - assert actual.rgba.g == expected.rgba.g - assert actual.rgba.b == expected.rgba.b - assert actual.rgba.a == expected.rgba.a - - -@pytest.mark.parametrize( - "constructor, value, string", - [ - (rgb, (10, 20, 30), "rgb(10, 20, 30)"), - (rgba, (10, 20, 30, 0.5), "rgba(10, 20, 30, 0.5)"), - (hsl, (10, 0.2, 0.3), "hsl(10, 0.2, 0.3)"), - (hsla, (10, 0.2, 0.3, 0.5), "hsla(10, 0.2, 0.3, 0.5)"), - ], -) -def test_repr(constructor, value, string): - assert repr(constructor(*value)) == string - - -def test_rgb_hash(): - assert hash(rgb(10, 20, 30)) == hash(rgb(10, 20, 30)) - assert hash(rgb(10, 20, 30)) != hash(rgb(30, 20, 10)) - - -def test_rgba_hash(): - assert hash(rgba(10, 20, 30, 0.5)) == hash(rgba(10, 20, 30, 0.5)) - assert hash(rgba(10, 20, 30, 1.0)) == hash(rgb(10, 20, 30)) - assert hash(rgb(10, 20, 30)) != hash(rgb(30, 20, 10)) - - -def test_hsl_hash(): - assert hash(hsl(10, 0.2, 0.3)) == hash(hsl(10, 0.2, 0.3)) - assert hash(hsl(10, 0.3, 0.2)) != hash(hsl(10, 0.2, 0.3)) - - -def test_hsla_hash(): - assert hash(hsla(10, 0.2, 0.3, 0.5)) == hash(hsla(10, 0.2, 0.3, 0.5)) - assert hash(hsla(10, 0.2, 0.3, 1.0)) == hash(hsl(10, 0.2, 0.3)) - assert hash(hsla(10, 0.3, 0.2, 0.5)) != hash(hsla(10, 0.2, 0.3, 0.5)) - assert hash(hsla(10, 0, 0, 0.5)) != hash(rgba(10, 0, 0, 0.5)) - - -@pytest.mark.parametrize( - "value, expected", - [ - # Blacks - ((0, 0.0, 0.0), (0x00, 0x00, 0x00)), - ((60, 0.0, 0.0), (0x00, 0x00, 0x00)), - ((180, 0.0, 0.0), (0x00, 0x00, 0x00)), - ((240, 0.0, 0.0), (0x00, 0x00, 0x00)), - ((360, 0.0, 0.0), (0x00, 0x00, 0x00)), - # Whites - ((0, 0.0, 1.0), (0xFF, 0xFF, 0xFF)), - ((60, 0.0, 1.0), (0xFF, 0xFF, 0xFF)), - ((180, 0.0, 1.0), (0xFF, 0xFF, 0xFF)), - ((240, 0.0, 1.0), (0xFF, 0xFF, 0xFF)), - ((360, 0.0, 1.0), (0xFF, 0xFF, 0xFF)), - # Grays - ((0, 0.0, 0.2), (0x33, 0x33, 0x33)), - ((0, 0.0, 0.4), (0x66, 0x66, 0x66)), - ((0, 0.0, 0.5), (0x80, 0x80, 0x80)), - ((0, 0.0, 0.6), (0x99, 0x99, 0x99)), - ((0, 0.0, 0.8), (0xCC, 0xCC, 0xCC)), - # Primaries - ((0, 1.0, 0.5), (0xFF, 0x00, 0x00)), - ((60, 1.0, 0.5), (0xFF, 0xFF, 0x00)), - ((120, 1.0, 0.5), (0x00, 0xFF, 0x00)), - ((180, 1.0, 0.5), (0x00, 0xFF, 0xFF)), - ((240, 1.0, 0.5), (0x00, 0x00, 0xFF)), - ((300, 1.0, 0.5), (0xFF, 0x00, 0xFF)), - ((360, 1.0, 0.5), (0xFF, 0x00, 0x00)), - # Muted - ((0, 0.25, 0.25), (0x50, 0x30, 0x30)), - ((60, 0.25, 0.25), (0x50, 0x50, 0x30)), - ((120, 0.25, 0.25), (0x30, 0x50, 0x30)), - ((180, 0.25, 0.25), (0x30, 0x50, 0x50)), - ((240, 0.25, 0.25), (0x30, 0x30, 0x50)), - ((300, 0.25, 0.25), (0x50, 0x30, 0x50)), - ((360, 0.25, 0.25), (0x50, 0x30, 0x30)), - ((0, 0.25, 0.75), (0xCF, 0xAF, 0xAF)), - ((60, 0.25, 0.75), (0xCF, 0xCF, 0xAF)), - ((120, 0.25, 0.75), (0xAF, 0xCF, 0xAF)), - ((180, 0.25, 0.75), (0xAF, 0xCF, 0xCF)), - ((240, 0.25, 0.75), (0xAF, 0xAF, 0xCF)), - ((300, 0.25, 0.75), (0xCF, 0xAF, 0xCF)), - ((360, 0.25, 0.75), (0xCF, 0xAF, 0xAF)), - ((0, 0.75, 0.75), (0xEF, 0x8F, 0x8F)), - ((60, 0.75, 0.75), (0xEF, 0xEF, 0x8F)), - ((120, 0.75, 0.75), (0x8F, 0xEF, 0x8F)), - ((180, 0.75, 0.75), (0x8F, 0xEF, 0xEF)), - ((240, 0.75, 0.75), (0x8F, 0x8F, 0xEF)), - ((300, 0.75, 0.75), (0xEF, 0x8F, 0xEF)), - ((360, 0.75, 0.75), (0xEF, 0x8F, 0x8F)), - ((0, 0.75, 0.25), (0x70, 0x10, 0x10)), - ((60, 0.75, 0.25), (0x70, 0x70, 0x10)), - ((120, 0.75, 0.25), (0x10, 0x70, 0x10)), - ((180, 0.75, 0.25), (0x10, 0x70, 0x70)), - ((240, 0.75, 0.25), (0x10, 0x10, 0x70)), - ((300, 0.75, 0.25), (0x70, 0x10, 0x70)), - ((360, 0.75, 0.25), (0x70, 0x10, 0x10)), - ], -) -def test_hsl(value, expected): - assert_equal_color(hsl(*value), rgb(*expected)) - - -@pytest.mark.parametrize( - "value, expected", - [ - ((60, 0.0, 0.0, 0.3), (0x00, 0x00, 0x00, 0.3)), - ((60, 0.0, 1.0, 0.3), (0xFF, 0xFF, 0xFF, 0.3)), - ((60, 1.0, 0.5, 0.3), (0xFF, 0xFF, 0x00, 0.3)), - ((60, 0.25, 0.25, 0.3), (0x50, 0x50, 0x30, 0.3)), - ((60, 0.25, 0.75, 0.3), (0xCF, 0xCF, 0xAF, 0.3)), - ((60, 0.75, 0.75, 0.3), (0xEF, 0xEF, 0x8F, 0.3)), - ((60, 0.75, 0.25, 0.3), (0x70, 0x70, 0x10, 0.3)), - ], -) -def test_hsl_alpha(value, expected): - assert_equal_color(hsla(*value), rgba(*expected)) - - -@pytest.mark.parametrize( - "constructor, value, name, min, max, actual", - [ - (rgb, (-1, 120, 10), "red", 0, 255, -1), - (rgb, (256, 120, 10), "red", 0, 255, 256), - (rgb, (120, -1, 10), "green", 0, 255, -1), - (rgb, (120, 256, 10), "green", 0, 255, 256), - (rgb, (120, 10, -1), "blue", 0, 255, -1), - (rgb, (120, 10, 256), "blue", 0, 255, 256), - # - (rgba, (-1, 120, 10, 0.5), "red", 0, 255, -1), - (rgba, (256, 120, 10, 0.5), "red", 0, 255, 256), - (rgba, (120, -1, 10, 0.5), "green", 0, 255, -1), - (rgba, (120, 256, 10, 0.5), "green", 0, 255, 256), - (rgba, (120, 10, -1, 0.5), "blue", 0, 255, -1), - (rgba, (120, 10, 256, 0.5), "blue", 0, 255, 256), - (rgba, (120, 10, 60, -0.5), "alpha", 0, 1, -0.5), - (rgba, (120, 10, 60, 1.1), "alpha", 0, 1, 1.1), - # - (hsl, (-1, 0.5, 0.8), "hue", 0, 360, -1), - (hsl, (361, 0.5, 0.8), "hue", 0, 360, 361), - (hsl, (120, -0.1, 0.8), "saturation", 0, 1, -0.1), - (hsl, (120, 1.1, 0.8), "saturation", 0, 1, 1.1), - (hsl, (120, 0.8, -0.1), "lightness", 0, 1, -0.1), - (hsl, (120, 0.8, 1.1), "lightness", 0, 1, 1.1), - # - (hsla, (-1, 0.5, 0.8, 0.5), "hue", 0, 360, -1), - (hsla, (361, 0.5, 0.8, 0.5), "hue", 0, 360, 361), - (hsla, (120, -0.1, 0.8, 0.5), "saturation", 0, 1, -0.1), - (hsla, (120, 1.1, 0.8, 0.5), "saturation", 0, 1, 1.1), - (hsla, (120, 0.8, -0.1, 0.5), "lightness", 0, 1, -0.1), - (hsla, (120, 0.8, 1.1, 0.5), "lightness", 0, 1, 1.1), - (hsla, (120, 0.8, 0.5, -0.1), "alpha", 0, 1, -0.1), - (hsla, (120, 0.8, 0.5, 1.1), "alpha", 0, 1, 1.1), - ], -) -def test_invalid_color_constructor(constructor, value, name, min, max, actual): - with pytest.raises( - ValueError, - match=rf"^{name} value should be between {min}-{max}\. Got {actual}$", - ): - constructor(*value) diff --git a/tests/colors/test_parsing.py b/tests/colors/test_parsing.py deleted file mode 100644 index 21c18eb..0000000 --- a/tests/colors/test_parsing.py +++ /dev/null @@ -1,171 +0,0 @@ -import pytest - -from travertino.colors import color, hsl, hsla, rgb, rgba - - -def assert_equal_hsl(value, expected): - # Nothing fancy - a color is equal if the attributes are all the same - actual = color(value) - assert actual.h == expected.h - assert actual.s == expected.s - assert actual.l == expected.l - assert actual.a == pytest.approx(expected.a, abs=0.001) - - -def assert_equal_rgb(value, expected): - # Nothing fancy - a color is equal if the attributes are all the same - actual = color(value) - assert actual.r == expected.r - assert actual.g == expected.g - assert actual.b == expected.b - assert actual.a == pytest.approx(expected.a, abs=0.001) - - -def test_noop(): - assert_equal_rgb(rgba(1, 2, 3, 0.5), rgba(1, 2, 3, 0.5)) - assert_equal_hsl(hsl(1, 0.2, 0.3), hsl(1, 0.2, 0.3)) - - -@pytest.mark.parametrize( - "value, expected", - [ - ("rgb(1,2,3)", (1, 2, 3)), - ("rgb(1, 2, 3)", (1, 2, 3)), - ("rgb( 1 , 2 , 3)", (1, 2, 3)), - ("#123", (0x11, 0x22, 0x33)), - ("#112233", (0x11, 0x22, 0x33)), - ("#abc", (0xAA, 0xBB, 0xCC)), - ("#ABC", (0xAA, 0xBB, 0xCC)), - ("#abcdef", (0xAB, 0xCD, 0xEF)), - ("#ABCDEF", (0xAB, 0xCD, 0xEF)), - ], -) -def test_rgb(value, expected): - assert_equal_rgb(value, rgb(*expected)) - - -@pytest.mark.parametrize( - "value", - [ - "10, 20", - "a, 10, 20", - "10, b, 20", - "10, 20, c", - "10, 20, 30, 0.5", - ], -) -def test_rgb_invalid(value): - with pytest.raises(ValueError): - color(f"rgb({value})") - - -@pytest.mark.parametrize( - "value, expected", - [ - ("rgba(1,2,3,0.5)", (1, 2, 3, 0.5)), - ("rgba(1, 2, 3, 0.5)", (1, 2, 3, 0.5)), - ("rgba( 1 , 2 , 3 , 0.5)", (1, 2, 3, 0.5)), - ("#1234", (0x11, 0x22, 0x33, 0.2666)), - ("#11223344", (0x11, 0x22, 0x33, 0.2666)), - ("#abcd", (0xAA, 0xBB, 0xCC, 0.8666)), - ("#ABCD", (0xAA, 0xBB, 0xCC, 0.8666)), - ("#abcdefba", (0xAB, 0xCD, 0xEF, 0.7294)), - ("#ABCDEFBA", (0xAB, 0xCD, 0xEF, 0.7294)), - ], -) -def test_rgba(value, expected): - assert_equal_rgb(value, rgba(*expected)) - - -@pytest.mark.parametrize( - "value", - [ - "10, 20, 30", - "a, 10, 20, 0.5", - "10, b, 20, 0.5", - "10, 20, c, 0.5", - "10, 20, 30, c", - "10, 20, 30, 0.5, 5", - ], -) -def test_rgba_invalid(value): - with pytest.raises(ValueError): - color(f"rgba({value})") - - -@pytest.mark.parametrize( - "value", - [ - "1,20%,30%", - "1, 20%, 30%", - "1, 20% , 30%", - ], -) -def test_hsl(value): - assert_equal_hsl(f"hsl({value})", hsl(1, 0.2, 0.3)) - - -@pytest.mark.parametrize( - "value", - [ - "1, 20%", - "a, 20%, 30%", - "1, a, 30%", - "1, 20%, a)", - "1, 20%, 30%, 0.5)", - ], -) -def test_hsl_invalid(value): - with pytest.raises(ValueError): - color(value) - - -@pytest.mark.parametrize( - "value", - [ - "1,20%,30%,0.5", - "1, 20%, 30%, 0.5", - " 1, 20% , 30% , 0.5", - ], -) -def test_hsla(value): - assert_equal_hsl(f"hsla({value})", hsla(1, 0.2, 0.3, 0.5)) - - -@pytest.mark.parametrize( - "value", - [ - "1, 20%, 30%", - "a, 20%, 30%, 0.5", - "1, a, 30%, 0.5", - "1, 20%, a, 0.5", - "1, 20%, 30%, a", - "1, 20%, 30%, 0.5, 5", - ], -) -def test_hsla_invalid(value): - with pytest.raises(ValueError): - color(f"hsla({value})") - - -@pytest.mark.parametrize( - "value, expected", - [ - ("Red", (0xFF, 0, 0)), - ("RED", (0xFF, 0, 0)), - ("red", (0xFF, 0, 0)), - ("rEd", (0xFF, 0, 0)), - ("CornflowerBlue", (0x64, 0x95, 0xED)), - ("cornflowerblue", (0x64, 0x95, 0xED)), - ("CORNFLOWERBLUE", (0x64, 0x95, 0xED)), - ("Cornflowerblue", (0x64, 0x95, 0xED)), - ("CoRnFlOwErBlUe", (0x64, 0x95, 0xED)), - ], -) -def test_named_color(value, expected): - assert_equal_rgb(value, rgb(*expected)) - - -def test_named_color_invalid(): - with pytest.raises(ValueError): - color("not a color") diff --git a/tests/fonts/__init__.py b/tests/fonts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fonts/test_constructor.py b/tests/fonts/test_constructor.py deleted file mode 100644 index 8729b51..0000000 --- a/tests/fonts/test_constructor.py +++ /dev/null @@ -1,203 +0,0 @@ -import pytest - -from travertino.constants import ( - BOLD, - ITALIC, - NORMAL, - OBLIQUE, - SMALL_CAPS, - SYSTEM_DEFAULT_FONT_SIZE, -) -from travertino.fonts import Font - - -def assert_font(font, family, size, style, variant, weight): - assert font.family == family - assert font.size == size - assert font.style == style - assert font.variant == variant - assert font.weight == weight - - -@pytest.mark.parametrize( - "font", - [ - Font("Comic Sans", "12 pt"), - Font("Comic Sans", 12), - Font("Comic Sans", 12, NORMAL, NORMAL, NORMAL), - ], -) -def test_equality(font): - assert font == Font("Comic Sans", "12 pt") - - -@pytest.mark.parametrize( - "font", - [ - Font("Comic Sans", 13), - Font("Comic Sans", 12, ITALIC), - Font("Times New Roman", 12, NORMAL, NORMAL, NORMAL), - "a string", - 5, - ], -) -def test_inqequality(font): - assert font != Font("Comic Sans", "12 pt") - - -def test_hash(): - assert hash(Font("Comic Sans", 12)) == hash(Font("Comic Sans", 12)) - - assert hash(Font("Comic Sans", 12, weight=BOLD)) != hash(Font("Comic Sans", 12)) - - -@pytest.mark.parametrize( - "size, kwargs, string", - [ - (12, {}, "12pt"), - (12, {"style": ITALIC}, "italic 12pt"), - (12, {"style": ITALIC, "variant": SMALL_CAPS}, "italic small-caps 12pt"), - ( - 12, - {"style": ITALIC, "variant": SMALL_CAPS, "weight": BOLD}, - "italic small-caps bold 12pt", - ), - (12, {"variant": SMALL_CAPS, "weight": BOLD}, "small-caps bold 12pt"), - (12, {"weight": BOLD}, "bold 12pt"), - (12, {"style": ITALIC, "weight": BOLD}, "italic bold 12pt"), - # Check system default size handling - (SYSTEM_DEFAULT_FONT_SIZE, {}, "system default size"), - (SYSTEM_DEFAULT_FONT_SIZE, {"style": ITALIC}, "italic system default size"), - ], -) -def test_repr(size, kwargs, string): - assert repr(Font("Comic Sans", size, **kwargs)) == f"" - - -@pytest.mark.parametrize("size", [12, "12", "12pt", "12 pt"]) -def test_simple_construction(size): - assert_font(Font("Comic Sans", size), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -def test_invalid_construction(): - with pytest.raises(ValueError): - Font("Comic Sans", "12 quatloos") - - -@pytest.mark.parametrize( - "family", - [ - "Comics Sans", - "Wingdings", - "'Comic Sans'", - '"Comic Sans"', - ], -) -def test_family(family): - normalized_family = family.replace("'", "").replace('"', "") - assert_font(Font(family, 12), normalized_family, 12, NORMAL, NORMAL, NORMAL) - - -@pytest.mark.parametrize( - "style, result_style", - [ - (ITALIC, ITALIC), - ("italic", ITALIC), - (OBLIQUE, OBLIQUE), - ("oblique", OBLIQUE), - ("something else", NORMAL), - ], -) -def test_style(style, result_style): - assert_font( - Font("Comic Sans", 12, style=style), - "Comic Sans", - 12, - result_style, - NORMAL, - NORMAL, - ) - - -@pytest.mark.parametrize( - "kwargs", - [ - {}, - {"style": ITALIC}, - ], -) -def test_make_normal_style(kwargs): - f = Font("Comic Sans", 12, **kwargs) - assert_font(f.normal_style(), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -@pytest.mark.parametrize( - "method, result", - [ - ("italic", ITALIC), - ("oblique", OBLIQUE), - ], -) -def test_make_slanted(method, result): - f = Font("Comic Sans", 12) - assert_font(getattr(f, method)(), "Comic Sans", 12, result, NORMAL, NORMAL) - - -@pytest.mark.parametrize( - "variant, result", - [ - (SMALL_CAPS, SMALL_CAPS), - ("small-caps", SMALL_CAPS), - ("something else", NORMAL), - ], -) -def test_variant(variant, result): - assert_font( - Font("Comic Sans", 12, variant=variant), - "Comic Sans", - 12, - NORMAL, - result, - NORMAL, - ) - - -@pytest.mark.parametrize("kwargs", [{}, {"variant": SMALL_CAPS}]) -def test_make_normal_variant(kwargs): - f = Font("Comic Sans", 12, **kwargs) - assert_font(f.normal_variant(), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -def test_make_small_caps(): - f = Font("Comic Sans", 12) - assert_font(f.small_caps(), "Comic Sans", 12, NORMAL, SMALL_CAPS, NORMAL) - - -@pytest.mark.parametrize( - "weight, result", - [ - (BOLD, BOLD), - ("bold", BOLD), - ("something else", NORMAL), - ], -) -def test_weight(weight, result): - assert_font( - Font("Comic Sans", 12, weight=weight), - "Comic Sans", - 12, - NORMAL, - NORMAL, - result, - ) - - -@pytest.mark.parametrize("kwargs", [{}, {"weight": BOLD}]) -def test_make_normal_weight(kwargs): - f = Font("Comic Sans", 12, **kwargs) - assert_font(f.normal_weight(), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -def test_make_bold(): - f = Font("Comic Sans", 12) - assert_font(f.bold(), "Comic Sans", 12, NORMAL, NORMAL, BOLD) diff --git a/tests/fonts/test_parsing.py b/tests/fonts/test_parsing.py deleted file mode 100644 index 1029f8e..0000000 --- a/tests/fonts/test_parsing.py +++ /dev/null @@ -1,133 +0,0 @@ -import pytest - -from tests.fonts.test_constructor import assert_font -from travertino.constants import ( - BOLD, - ITALIC, - NORMAL, - OBLIQUE, - SMALL_CAPS, -) -from travertino.fonts import Font, font - - -def test_font_instance(): - f = Font("Comic Sans", 12) - - parsed = font(f) - - assert f == parsed - assert f is parsed - - -@pytest.mark.parametrize( - "string, style, variant, weight", - [ - ("12pt Comic Sans", NORMAL, NORMAL, NORMAL), - ("italic 12pt Comic Sans", ITALIC, NORMAL, NORMAL), - ("italic small-caps 12pt Comic Sans", ITALIC, SMALL_CAPS, NORMAL), - ("italic small-caps bold 12pt Comic Sans", ITALIC, SMALL_CAPS, BOLD), - ("small-caps bold 12pt Comic Sans", NORMAL, SMALL_CAPS, BOLD), - ("italic bold 12 pt Comic Sans", ITALIC, NORMAL, BOLD), - ("bold 12 pt Comic Sans", NORMAL, NORMAL, BOLD), - ], -) -def test_successful_combinations(string, style, variant, weight): - assert_font(font(string), "Comic Sans", 12, style, variant, weight) - - -@pytest.mark.parametrize( - "string", - [ - "12pt Comic Sans", - "12 pt Comic Sans", - "12 Comic Sans", - ], -) -def test_font_sizes(string): - assert_font(font(string), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -def test_invalid_size(): - with pytest.raises(ValueError): - font("12quatloo Comic Sans") - - -@pytest.mark.parametrize("string", ["12pt 'Comic Sans'", '12pt "Comic Sans"']) -def test_font_family(string): - assert_font(font(string), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) - - -@pytest.mark.parametrize( - "string, style, variant", - [ - ("normal 12pt Comic Sans", NORMAL, NORMAL), - ("italic normal 12pt Comic Sans", ITALIC, NORMAL), - ("italic small-caps normal 12pt Comic Sans", ITALIC, SMALL_CAPS), - ], -) -def test_normal(string, style, variant): - assert_font(font(string), "Comic Sans", 12, style, variant, NORMAL) - - -@pytest.mark.parametrize( - "string, style", - [ - ("italic 12pt Comic Sans", ITALIC), - ("oblique 12pt Comic Sans", OBLIQUE), - ], -) -def test_style(string, style): - assert_font(font(string), "Comic Sans", 12, style, NORMAL, NORMAL) - - -def test_invalid_style(): - with pytest.raises(ValueError): - font("wiggly small-caps bold 12pt Comic Sans") - - -def test_variant(): - assert_font( - font("italic small-caps 12pt Comic Sans"), - "Comic Sans", - 12, - ITALIC, - SMALL_CAPS, - NORMAL, - ) - - with pytest.raises(ValueError): - font("italic wiggly bold 12pt Comic Sans") - - -def test_weight(): - assert_font( - font("italic small-caps bold 12pt Comic Sans"), - "Comic Sans", - 12, - ITALIC, - SMALL_CAPS, - BOLD, - ) - - with pytest.raises(ValueError): - font("italic small-caps wiggly 12pt Comic Sans") - - -@pytest.mark.parametrize( - "string", - [ - "oblique italic 12pt Comic Sans", - "italic small-caps oblique 12pt Comic Sans", - "italic small-caps bold small-caps 12pt Comic Sans", - "bold bold 12pt Comic Sans", - ], -) -def test_duplicates(string): - with pytest.raises(ValueError): - font(string) - - -def test_invaid(): - with pytest.raises(ValueError): - font(42) diff --git a/tests/test_choices.py b/tests/test_choices.py deleted file mode 100644 index 9eea0f8..0000000 --- a/tests/test_choices.py +++ /dev/null @@ -1,385 +0,0 @@ -from __future__ import annotations - -from warnings import catch_warnings, filterwarnings - -import pytest - -from tests.utils import mock_attr, prep_style_class -from travertino.colors import NAMED_COLOR, rgb -from travertino.constants import GOLDENROD, NONE, REBECCAPURPLE, TOP -from travertino.declaration import BaseStyle, Choices, validated_property - - -@prep_style_class -class Style(BaseStyle): - none: str = validated_property(choices=Choices(NONE, REBECCAPURPLE), initial=NONE) - allow_string: str = validated_property( - choices=Choices(string=True), initial="start" - ) - allow_integer: int = validated_property(choices=Choices(integer=True), initial=0) - allow_number: float = validated_property(choices=Choices(number=True), initial=0) - allow_color: str = validated_property( - choices=Choices(color=True), initial="goldenrod" - ) - values: str = validated_property(choices=Choices("a", "b", NONE), initial="a") - multiple_choices: str | float = validated_property( - choices=Choices("a", "b", NONE, number=True, color=True), - initial=None, - ) - string_symbol: str = validated_property(choices=Choices(TOP, NONE)) - - -with catch_warnings(): - filterwarnings("ignore", category=DeprecationWarning) - - @mock_attr("apply") - class DeprecatedStyle(BaseStyle): - pass - - DeprecatedStyle.validated_property( - "none", choices=Choices(NONE, REBECCAPURPLE), initial=NONE - ) - DeprecatedStyle.validated_property( - "allow_string", choices=Choices(string=True), initial="start" - ) - DeprecatedStyle.validated_property( - "allow_integer", choices=Choices(integer=True), initial=0 - ) - DeprecatedStyle.validated_property( - "allow_number", choices=Choices(number=True), initial=0 - ) - DeprecatedStyle.validated_property( - "allow_color", choices=Choices(color=True), initial="goldenrod" - ) - DeprecatedStyle.validated_property( - "values", choices=Choices("a", "b", NONE), initial="a" - ) - DeprecatedStyle.validated_property( - "multiple_choices", - choices=Choices("a", "b", NONE, number=True, color=True), - initial=None, - ) - DeprecatedStyle.validated_property("string_symbol", choices=Choices(TOP, NONE)) - - -def assert_property(obj, name, value): - assert getattr(obj, name) == value - - obj.apply.assert_called_once_with(name, value) - obj.apply.reset_mock() - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_none(StyleClass): - style = StyleClass() - assert style.none == NONE - - with pytest.raises(ValueError): - style.none = 10 - - with pytest.raises(ValueError): - style.none = 3.14159 - - with pytest.raises(ValueError): - style.none = "#112233" - - with pytest.raises(ValueError): - style.none = "a" - - with pytest.raises(ValueError): - style.none = "b" - - # Set the property to a different explicit value - style.none = REBECCAPURPLE - assert_property(style, "none", REBECCAPURPLE) - - # A Travertino NONE is an explicit value - style.none = NONE - assert_property(style, "none", NONE) - - # Set the property to a different explicit value - style.none = REBECCAPURPLE - assert_property(style, "none", REBECCAPURPLE) - - # A Python None is invalid - with pytest.raises(ValueError): - style.none = None - - # The property can be reset - del style.none - assert_property(style, "none", NONE) - - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property none; Valid values are: " - r"none, rebeccapurple", - ): - style.none = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_allow_string(StyleClass): - style = StyleClass() - assert style.allow_string == "start" - - with pytest.raises(ValueError): - style.allow_string = 10 - - with pytest.raises(ValueError): - style.allow_string = 3.14159 - - style.allow_string = REBECCAPURPLE - assert_property(style, "allow_string", "rebeccapurple") - - style.allow_string = "#112233" - assert_property(style, "allow_string", "#112233") - - style.allow_string = "a" - assert_property(style, "allow_string", "a") - - style.allow_string = "b" - assert_property(style, "allow_string", "b") - - # A Travertino NONE is an explicit string value - style.allow_string = NONE - assert_property(style, "allow_string", NONE) - - # A Python None is invalid - with pytest.raises(ValueError): - style.allow_string = None - - # The property can be reset - del style.allow_string - assert_property(style, "allow_string", "start") - - with pytest.raises( - ValueError, - match=r"Invalid value 99 for property allow_string; Valid values are: ", - ): - style.allow_string = 99 - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_allow_integer(StyleClass): - style = StyleClass() - assert style.allow_integer == 0 - - style.allow_integer = 10 - assert_property(style, "allow_integer", 10) - - # This is an odd case; Python happily rounds floats to integers. - # It's more trouble than it's worth to correct this. - style.allow_integer = 3.14159 - assert_property(style, "allow_integer", 3) - - with pytest.raises(ValueError): - style.allow_integer = REBECCAPURPLE - - with pytest.raises(ValueError): - style.allow_integer = "#112233" - - with pytest.raises(ValueError): - style.allow_integer = "a" - - with pytest.raises(ValueError): - style.allow_integer = "b" - - # A Travertino NONE is an explicit string value - with pytest.raises(ValueError): - style.allow_integer = NONE - - # A Python None is invalid - with pytest.raises(ValueError): - style.allow_integer = None - - # The property can be reset - del style.allow_integer - assert_property(style, "allow_integer", 0) - - # Check the error message - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property allow_integer; Valid values are: ", - ): - style.allow_integer = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_allow_number(StyleClass): - style = StyleClass() - assert style.allow_number == 0 - - style.allow_number = 10 - assert_property(style, "allow_number", 10.0) - - style.allow_number = 3.14159 - assert_property(style, "allow_number", 3.14159) - - with pytest.raises(ValueError): - style.allow_number = REBECCAPURPLE - - with pytest.raises(ValueError): - style.allow_number = "#112233" - - with pytest.raises(ValueError): - style.allow_number = "a" - - with pytest.raises(ValueError): - style.allow_number = "b" - - # A Travertino NONE is an explicit string value - with pytest.raises(ValueError): - style.allow_number = NONE - - # A Python None is invalid - with pytest.raises(ValueError): - style.allow_number = None - - # The property can be reset - del style.allow_number - assert_property(style, "allow_number", 0) - - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property allow_number; Valid values are: ", - ): - style.allow_number = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_allow_color(StyleClass): - style = StyleClass() - assert style.allow_color == NAMED_COLOR[GOLDENROD] - - with pytest.raises(ValueError): - style.allow_color = 10 - - with pytest.raises(ValueError): - style.allow_color = 3.14159 - - style.allow_color = REBECCAPURPLE - assert_property(style, "allow_color", NAMED_COLOR[REBECCAPURPLE]) - - style.allow_color = "#112233" - assert_property(style, "allow_color", rgb(0x11, 0x22, 0x33)) - - with pytest.raises(ValueError): - style.allow_color = "a" - - with pytest.raises(ValueError): - style.allow_color = "b" - - # A Travertino NONE is an explicit string value - with pytest.raises(ValueError): - style.allow_color = NONE - - # A Python None is invalid - with pytest.raises(ValueError): - style.allow_color = None - - # The property can be reset - del style.allow_color - assert_property(style, "allow_color", NAMED_COLOR["goldenrod"]) - - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property allow_color; Valid values are: ", - ): - style.allow_color = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_values(StyleClass): - style = StyleClass() - assert style.values == "a" - - with pytest.raises(ValueError): - style.values = 10 - - with pytest.raises(ValueError): - style.values = 3.14159 - - with pytest.raises(ValueError): - style.values = REBECCAPURPLE - - with pytest.raises(ValueError): - style.values = "#112233" - - style.values = NONE - assert_property(style, "values", NONE) - - style.values = "b" - assert_property(style, "values", "b") - - # A Python None is invalid - with pytest.raises(ValueError): - style.values = None - - # The property can be reset - del style.values - assert_property(style, "values", "a") - - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property values; Valid values are: a, b, none", - ): - style.values = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_multiple_choices(StyleClass): - style = StyleClass() - - style.multiple_choices = 10 - assert_property(style, "multiple_choices", 10.0) - - style.multiple_choices = 3.14159 - assert_property(style, "multiple_choices", 3.14159) - - style.multiple_choices = REBECCAPURPLE - assert_property(style, "multiple_choices", NAMED_COLOR[REBECCAPURPLE]) - - style.multiple_choices = "#112233" - assert_property(style, "multiple_choices", rgb(0x11, 0x22, 0x33)) - - style.multiple_choices = "a" - assert_property(style, "multiple_choices", "a") - - style.multiple_choices = NONE - assert_property(style, "multiple_choices", NONE) - - style.multiple_choices = "b" - assert_property(style, "multiple_choices", "b") - - # A Python None is invalid - with pytest.raises(ValueError): - style.multiple_choices = None - - # The property can be reset - # There's no initial value, so the property is None - del style.multiple_choices - assert style.multiple_choices is None - - # Check the error message - with pytest.raises( - ValueError, - match=r"Invalid value 'invalid' for property multiple_choices; Valid values are: " - r"a, b, none, , ", - ): - style.multiple_choices = "invalid" - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_string_symbol(StyleClass): - style = StyleClass() - - # Set a symbolic value using the string value of the symbol - # We can't just use the string directly, though - that would - # get optimized by the compiler. So we create a string and - # transform it into the value we want. - val = "TOP" - style.string_symbol = val.lower() - - # Both equality and instance checking should work. - assert_property(style, "string_symbol", TOP) - assert style.string_symbol is TOP diff --git a/tests/test_declaration.py b/tests/test_declaration.py deleted file mode 100644 index c2f1414..0000000 --- a/tests/test_declaration.py +++ /dev/null @@ -1,836 +0,0 @@ -from __future__ import annotations - -from unittest.mock import call -from warnings import catch_warnings, filterwarnings - -import pytest - -from tests.utils import mock_attr, prep_style_class -from travertino.declaration import ( - BaseStyle, - Choices, - ImmutableList, - directional_property, - list_property, - validated_property, -) - -VALUE1 = "value1" -VALUE2 = "value2" -VALUE3 = "value3" -VALUE_CHOICES = Choices(VALUE1, VALUE2, VALUE3, None, integer=True) -DEFAULT_VALUE_CHOICES = Choices(VALUE1, VALUE2, VALUE3, integer=True) - - -@prep_style_class -class Style(BaseStyle): - # Some properties with explicit initial values - explicit_const: str | int = validated_property( - choices=VALUE_CHOICES, initial=VALUE1 - ) - explicit_value: str | int = validated_property(choices=VALUE_CHOICES, initial=0) - explicit_none: str | int | None = validated_property( - choices=VALUE_CHOICES, initial=None - ) - - # A property with an implicit default value. - # This usually means the default is platform specific. - implicit: str | int | None = validated_property(choices=DEFAULT_VALUE_CHOICES) - - # A set of directional properties - thing: tuple[str | int] | str | int = directional_property("thing{}") - thing_top: str | int = validated_property(choices=VALUE_CHOICES, initial=0) - thing_right: str | int = validated_property(choices=VALUE_CHOICES, initial=0) - thing_bottom: str | int = validated_property(choices=VALUE_CHOICES, initial=0) - thing_left: str | int = validated_property(choices=VALUE_CHOICES, initial=0) - - # Doesn't need to be tested in deprecated API: - list_prop: list[str] = list_property(choices=VALUE_CHOICES, initial=(VALUE2,)) - - -with catch_warnings(): - filterwarnings("ignore", category=DeprecationWarning) - - @mock_attr("apply") - class DeprecatedStyle(BaseStyle): - pass - - # Some properties with explicit initial values - DeprecatedStyle.validated_property( - "explicit_const", choices=VALUE_CHOICES, initial=VALUE1 - ) - DeprecatedStyle.validated_property( - "explicit_value", choices=VALUE_CHOICES, initial=0 - ) - DeprecatedStyle.validated_property( - "explicit_none", choices=VALUE_CHOICES, initial=None - ) - - # A property with an implicit default value. - # This usually means the default is platform specific. - DeprecatedStyle.validated_property("implicit", choices=DEFAULT_VALUE_CHOICES) - - # A set of directional properties - DeprecatedStyle.validated_property("thing_top", choices=VALUE_CHOICES, initial=0) - DeprecatedStyle.validated_property("thing_right", choices=VALUE_CHOICES, initial=0) - DeprecatedStyle.validated_property("thing_bottom", choices=VALUE_CHOICES, initial=0) - DeprecatedStyle.validated_property("thing_left", choices=VALUE_CHOICES, initial=0) - DeprecatedStyle.directional_property("thing%s") - - -class StyleSubclass(Style): - pass - - -class DeprecatedStyleSubclass(DeprecatedStyle): - pass - - -class Sibling(BaseStyle): - pass - - -@prep_style_class -@mock_attr("reapply") -class MockedReapplyStyle(BaseStyle): - pass - - -def test_invalid_style(): - with pytest.raises(ValueError): - # Define an invalid initial value on a validated property - validated_property(choices=VALUE_CHOICES, initial="something") - - with pytest.raises(ValueError): - # Same for list property - list_property(choices=VALUE_CHOICES, initial=["something"]) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_positional_argument(StyleClass): - # Could be the subclass or inherited __init__, depending on Python version / API - # used. - with pytest.raises( - TypeError, match=r"__init__\(\) takes 1 positional argument but 2 were given" - ): - StyleClass(5) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_create_and_copy(StyleClass): - style = StyleClass(explicit_const=VALUE2, implicit=VALUE3) - - dup = style.copy() - assert dup.explicit_const == VALUE2 - assert dup.explicit_value == 0 - assert dup.implicit == VALUE3 - - -def test_deprecated_copy(): - style = MockedReapplyStyle() - - with pytest.warns(DeprecationWarning): - style_copy = style.copy(applicator=object()) - - style_copy.reapply.assert_called_once() - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_reapply(StyleClass): - style = StyleClass(explicit_const=VALUE2, implicit=VALUE3) - - style.reapply() - style.apply.assert_has_calls( - [ - call("explicit_const", VALUE2), - call("explicit_value", 0), - call("explicit_none", None), - call("implicit", VALUE3), - call("thing_left", 0), - call("thing_top", 0), - call("thing_right", 0), - call("thing_bottom", 0), - ], - any_order=True, - ) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_property_with_explicit_const(StyleClass): - style = StyleClass() - - # Default value is VALUE1 - assert style.explicit_const is VALUE1 - style.apply.assert_not_called() - - # Modify the value - style.explicit_const = 10 - - assert style.explicit_const == 10 - style.apply.assert_called_once_with("explicit_const", 10) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set the value to the same value. - # No dirty notification is sent - style.explicit_const = 10 - assert style.explicit_const == 10 - style.apply.assert_not_called() - - # Set the value to something new - # A dirty notification is set. - style.explicit_const = 20 - assert style.explicit_const == 20 - style.apply.assert_called_once_with("explicit_const", 20) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear the property - del style.explicit_const - assert style.explicit_const is VALUE1 - style.apply.assert_called_once_with("explicit_const", VALUE1) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear the property again. - # The underlying attribute won't exist, so this - # should be a no-op. - del style.explicit_const - assert style.explicit_const is VALUE1 - style.apply.assert_not_called() - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_property_with_explicit_value(StyleClass): - style = StyleClass() - - # Default value is 0 - assert style.explicit_value == 0 - style.apply.assert_not_called() - - # Modify the value - style.explicit_value = 10 - - assert style.explicit_value == 10 - style.apply.assert_called_once_with("explicit_value", 10) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set the value to the same value. - # No dirty notification is sent - style.explicit_value = 10 - assert style.explicit_value == 10 - style.apply.assert_not_called() - - # Set the value to something new - # A dirty notification is set. - style.explicit_value = 20 - assert style.explicit_value == 20 - style.apply.assert_called_once_with("explicit_value", 20) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear the property - del style.explicit_value - assert style.explicit_value == 0 - style.apply.assert_called_once_with("explicit_value", 0) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_property_with_explicit_none(StyleClass): - style = StyleClass() - - # Default value is None - assert style.explicit_none is None - style.apply.assert_not_called() - - # Modify the value - style.explicit_none = 10 - - assert style.explicit_none == 10 - style.apply.assert_called_once_with("explicit_none", 10) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set the property to the same value. - # No dirty notification is sent - style.explicit_none = 10 - assert style.explicit_none == 10 - style.apply.assert_not_called() - - # Set the property to something new - # A dirty notification is set. - style.explicit_none = 20 - assert style.explicit_none == 20 - style.apply.assert_called_once_with("explicit_none", 20) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear the property - del style.explicit_none - assert style.explicit_none is None - style.apply.assert_called_once_with("explicit_none", None) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_property_with_implicit_default(StyleClass): - style = StyleClass() - - # Default value is None - assert style.implicit is None - style.apply.assert_not_called() - - # Modify the value - style.implicit = 10 - - assert style.implicit == 10 - style.apply.assert_called_once_with("implicit", 10) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set the value to the same value. - # No dirty notification is sent - style.implicit = 10 - assert style.implicit == 10 - style.apply.assert_not_called() - - # Set the value to something new - # A dirty notification is set. - style.implicit = 20 - assert style.implicit == 20 - style.apply.assert_called_once_with("implicit", 20) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear the property - del style.implicit - assert style.implicit is None - style.apply.assert_called_once_with("implicit", None) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_set_initial_no_apply(StyleClass): - """If a property hasn't been set, assigning it its initial value shouldn't apply.""" - style = StyleClass() - - # 0 is the initial value - style.explicit_value = 0 - - style.apply.assert_not_called() - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_directional_property(StyleClass): - style = StyleClass() - - # Default value is 0 - assert style.thing == (0, 0, 0, 0) - assert style.thing_top == 0 - assert style.thing_right == 0 - assert style.thing_bottom == 0 - assert style.thing_left == 0 - style.apply.assert_not_called() - - # Set a value in one axis - style.thing_top = 10 - - assert style.thing == (10, 0, 0, 0) - assert style.thing_top == 10 - assert style.thing_right == 0 - assert style.thing_bottom == 0 - assert style.thing_left == 0 - style.apply.assert_called_once_with("thing_top", 10) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set a value directly with a single item - style.thing = (10,) - - assert style.thing == (10, 10, 10, 10) - assert style.thing_top == 10 - assert style.thing_right == 10 - assert style.thing_bottom == 10 - assert style.thing_left == 10 - style.apply.assert_has_calls( - [ - call("thing_right", 10), - call("thing_bottom", 10), - call("thing_left", 10), - ] - ) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set a value directly with a single item - style.thing = 30 - - assert style.thing == (30, 30, 30, 30) - assert style.thing_top == 30 - assert style.thing_right == 30 - assert style.thing_bottom == 30 - assert style.thing_left == 30 - style.apply.assert_has_calls( - [ - call("thing_top", 30), - call("thing_right", 30), - call("thing_bottom", 30), - call("thing_left", 30), - ] - ) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set a value directly with a 2 values - style.thing = (10, 20) - - assert style.thing == (10, 20, 10, 20) - assert style.thing_top == 10 - assert style.thing_right == 20 - assert style.thing_bottom == 10 - assert style.thing_left == 20 - style.apply.assert_has_calls( - [ - call("thing_top", 10), - call("thing_right", 20), - call("thing_bottom", 10), - call("thing_left", 20), - ] - ) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set a value directly with a 3 values - style.thing = (10, 20, 30) - - assert style.thing == (10, 20, 30, 20) - assert style.thing_top == 10 - assert style.thing_right == 20 - assert style.thing_bottom == 30 - assert style.thing_left == 20 - style.apply.assert_called_once_with("thing_bottom", 30) - - # Clear the applicator mock - style.apply.reset_mock() - - # Set a value directly with a 4 values - style.thing = (10, 20, 30, 40) - - assert style.thing == (10, 20, 30, 40) - assert style.thing_top == 10 - assert style.thing_right == 20 - assert style.thing_bottom == 30 - assert style.thing_left == 40 - style.apply.assert_called_once_with("thing_left", 40) - - # Set a value directly with an invalid number of values - with pytest.raises(ValueError): - style.thing = () - - with pytest.raises(ValueError): - style.thing = (10, 20, 30, 40, 50) - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear a value on one axis - del style.thing_top - - assert style.thing == (0, 20, 30, 40) - assert style.thing_top == 0 - assert style.thing_right == 20 - assert style.thing_bottom == 30 - assert style.thing_left == 40 - style.apply.assert_called_once_with("thing_top", 0) - - # Restore the top thing - style.thing_top = 10 - - # Clear the applicator mock - style.apply.reset_mock() - - # Clear a value directly - del style.thing - - assert style.thing == (0, 0, 0, 0) - assert style.thing_top == 0 - assert style.thing_right == 0 - assert style.thing_bottom == 0 - assert style.thing_left == 0 - style.apply.assert_has_calls( - [ - call("thing_right", 0), - call("thing_bottom", 0), - call("thing_left", 0), - ] - ) - - -@pytest.mark.parametrize( - "value, expected", - [ - ([VALUE1], [VALUE1]), - (VALUE1, [VALUE1]), - ([VALUE1, VALUE3], [VALUE1, VALUE3]), - ([VALUE2, VALUE1], [VALUE2, VALUE1]), - ([VALUE2, VALUE3, 1, 2, VALUE1], [VALUE2, VALUE3, 1, 2, VALUE1]), - # Duplicates are kept, but "normalized" via validation. - ( - [VALUE3, 1, VALUE3, "1", True, " 1", VALUE2], - [VALUE3, 1, VALUE3, 1, 1, 1, VALUE2], - ), - # Other sequences should work too. - ((VALUE1, VALUE3), [VALUE1, VALUE3]), - ], -) -def test_list_property(value, expected): - style = Style() - style.list_prop = value - assert style.list_prop == expected - - -@pytest.mark.parametrize( - "value, error, match", - [ - ( - 5, - TypeError, - r"Value for list property list_prop must be a sequence\.", - ), - ( - # Fails because it's only a generator, not a comprehension: - (i for i in [VALUE1, VALUE3]), - TypeError, - r"Value for list property list_prop must be a sequence.", - ), - ( - [VALUE3, VALUE1, "bogus"], - ValueError, - r"Invalid item value 'bogus' for list property list_prop; " - r"Valid values are: none, value1, value2, value3, ", - ), - ( - (), - ValueError, - r"List properties cannot be set to an empty sequence; " - r"to reset a property, use del `style.list_prop`\.", - ), - ( - [], - ValueError, - r"List properties cannot be set to an empty sequence; " - r"to reset a property, use del `style.list_prop`\.", - ), - ], -) -def test_list_property_invalid(value, error, match): - style = Style() - with pytest.raises(error, match=match): - style.list_prop = value - - -def test_list_property_immutable(): - style = Style() - style.list_prop = [1, 2, 3, VALUE2] - prop = style.list_prop - - with pytest.raises(TypeError, match=r"does not support item assignment"): - prop[0] = 5 - - with pytest.raises(TypeError, match=r"doesn't support item deletion"): - del prop[1] - - with pytest.raises(AttributeError): - prop.insert(2, VALUE1) - - with pytest.raises(AttributeError): - prop.append(VALUE3) - - with pytest.raises(AttributeError): - prop.clear() - - with pytest.raises(AttributeError): - prop.reverse() - - with pytest.raises(AttributeError): - prop.pop() - - with pytest.raises(AttributeError): - prop.remove(VALUE2) - - with pytest.raises(AttributeError): - prop.extend([5, 6, 7]) - - with pytest.raises(TypeError, match=r"unsupported operand type\(s\)"): - prop += [4, 3, VALUE1] - - with pytest.raises(TypeError, match=r"unsupported operand type\(s\)"): - prop += ImmutableList([4, 3, VALUE1]) - - with pytest.raises(AttributeError): - prop.sort() - - -def test_list_property_list_like(): - style = Style() - style.list_prop = [1, 2, 3, VALUE2] - prop = style.list_prop - - assert isinstance(prop, ImmutableList) - assert prop == [1, 2, 3, VALUE2] - assert prop == ImmutableList([1, 2, 3, VALUE2]) - assert str(prop) == repr(prop) == "[1, 2, 3, 'value2']" - assert len(prop) == 4 - - count = 0 - for _ in prop: - count += 1 - assert count == 4 - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_set_multiple_properties(StyleClass): - style = StyleClass() - - # Set a pair of properties - style.update(explicit_value=20, explicit_none=10) - - assert style.explicit_const is VALUE1 - assert style.explicit_none == 10 - assert style.explicit_value == 20 - style.apply.assert_has_calls( - [ - call("explicit_value", 20), - call("explicit_none", 10), - ], - any_order=True, - ) - - # Set a different pair of properties - style.update(explicit_const=VALUE2, explicit_value=30) - - assert style.explicit_const is VALUE2 - assert style.explicit_value == 30 - assert style.explicit_none == 10 - style.apply.assert_has_calls( - [ - call("explicit_const", VALUE2), - call("explicit_value", 30), - ], - any_order=True, - ) - - # Clear the applicator mock - style.apply.reset_mock() - - # Setting a non-property - with pytest.raises(NameError): - style.update(not_a_property=10) - - style.apply.assert_not_called() - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_str(StyleClass): - style = StyleClass() - - style.update( - explicit_const=VALUE2, - explicit_value=20, - thing=(30, 40, 50, 60), - ) - - assert ( - str(style) == "explicit-const: value2; " - "explicit-value: 20; " - "thing-bottom: 50; " - "thing-left: 60; " - "thing-right: 40; " - "thing-top: 30" - ) - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -def test_dict(StyleClass): - "Style declarations expose a dict-like interface" - style = StyleClass() - - style.update( - explicit_const=VALUE2, - explicit_value=20, - thing=(30, 40, 50, 60), - ) - - expected_keys = { - "explicit_const", - "explicit_value", - "thing_bottom", - "thing_left", - "thing_right", - "thing_top", - } - - assert style.keys() == expected_keys - - assert sorted(style.items()) == sorted( - [ - ("explicit_const", "value2"), - ("explicit_value", 20), - ("thing_bottom", 50), - ("thing_left", 60), - ("thing_right", 40), - ("thing_top", 30), - ] - ) - - # Properties that are set are in the keys. - for name in expected_keys: - assert name in style - - # Directional properties with one or more of the aliased properties set also count. - assert "thing" in style - - # Valid properties that haven't been set are not in the keys. - assert "implicit" not in style - assert "explicit_none" not in style - - # Neither are invalid properties. - assert "invalid_property" not in style - - # A property can be set, retrieved and cleared using the attribute name - style["thing-bottom"] = 10 - assert style["thing-bottom"] == 10 - del style["thing-bottom"] - assert style["thing-bottom"] == 0 - - # A property can be set, retrieved and cleared using the Python attribute name - style["thing_bottom"] = 10 - assert style["thing_bottom"] == 10 - del style["thing_bottom"] - assert style["thing_bottom"] == 0 - - # Property aliases can be accessed as well. - style["thing"] = 5 - assert style["thing"] == (5, 5, 5, 5) - del style["thing"] - assert style["thing"] == (0, 0, 0, 0) - - # Clearing a valid property isn't an error - del style["thing_bottom"] - assert style["thing_bottom"] == 0 - - # Non-existent properties raise KeyError - with pytest.raises(KeyError): - style["no-such-property"] = "no-such-value" - - with pytest.raises(KeyError): - style["no-such-property"] - - with pytest.raises(KeyError): - del style["no-such-property"] - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -@pytest.mark.parametrize("instantiate", [True, False]) -def test_union_operators(StyleClass, instantiate): - """Styles support | and |= with dicts and with their own class.""" - left = StyleClass(explicit_value=VALUE1, implicit=VALUE2) - - style_dict = {"thing_top": 5, "implicit": VALUE3} - right = StyleClass(**style_dict) if instantiate else style_dict - - # Standard operator - result = left | right - - # Original objects unchanged - assert left["explicit_value"] == VALUE1 - assert left["implicit"] == VALUE2 - - assert right["thing_top"] == 5 - assert right["implicit"] == VALUE3 - - # Unshared properties assigned - assert result["explicit_const"] == VALUE1 - assert result["thing_top"] == 5 - - # Common property overridden by second operand - assert result["implicit"] == VALUE3 - - # In-place version - left |= right - - # Common property updated on lefthand - assert left["explicit_value"] == VALUE1 - assert left["implicit"] == VALUE3 - - # Righthand unchanged - assert right["thing_top"] == 5 - assert right["implicit"] == VALUE3 - - -@pytest.mark.parametrize( - "StyleClass, OtherClass", - [ - (Style, StyleSubclass), - (Style, Sibling), - (Style, int), - (Style, list), - (DeprecatedStyle, DeprecatedStyleSubclass), - (DeprecatedStyle, Sibling), - (DeprecatedStyle, int), - (DeprecatedStyle, list), - ], -) -def test_union_operators_invalid_type(StyleClass, OtherClass): - """Styles do not support | or |= with other style classes or with non-mappings.""" - - left = StyleClass() - right = OtherClass() - - with pytest.raises(TypeError, match=r"unsupported operand type"): - left | right - - with pytest.raises(TypeError, match=r"unsupported operand type"): - left |= right - - -@pytest.mark.parametrize("StyleClass", [Style, DeprecatedStyle]) -@pytest.mark.parametrize( - "right, error", - [ - ({"implicit": "bogus_value"}, ValueError), - ({"bogus_key": 3.12}, NameError), - ], -) -def test_union_operators_invalid_key_value(StyleClass, right, error): - """Operators will accept any mapping, but invalid keys/values are still an error.""" - left = StyleClass() - - with pytest.raises(error): - left | right - - with pytest.raises(error): - left |= right - - -def test_deprecated_class_methods(): - class OldStyle(BaseStyle): - pass - - with pytest.warns(DeprecationWarning): - OldStyle.validated_property("implicit", choices=DEFAULT_VALUE_CHOICES) - - with pytest.warns(DeprecationWarning): - OldStyle.directional_property("thing%s") diff --git a/tests/test_layout.py b/tests/test_layout.py deleted file mode 100644 index d6c417f..0000000 --- a/tests/test_layout.py +++ /dev/null @@ -1,407 +0,0 @@ -import pytest - -from travertino.declaration import BaseStyle -from travertino.layout import BaseBox, Viewport -from travertino.node import Node -from travertino.size import BaseIntrinsicSize - - -class Style(BaseStyle): - class IntrinsicSize(BaseIntrinsicSize): - pass - - class Box(BaseBox): - pass - - -def test_viewport_default(): - viewport = Viewport() - - assert viewport.width == 0 - assert viewport.height == 0 - assert viewport.dpi is None - - -def test_viewport_constructor(): - viewport = Viewport(width=640, height=480, dpi=96) - - assert viewport.width == 640 - assert viewport.height == 480 - assert viewport.dpi == 96 - - -class TestBox: - pass - - -@pytest.fixture -def box(): - box = TestBox() - - box.maxDiff = None - - box.grandchild1_1 = Node(style=Style()) - box.grandchild1_1.layout.min_content_width = 5 - box.grandchild1_1.layout.content_width = 10 - box.grandchild1_1.layout.min_content_height = 8 - box.grandchild1_1.layout.content_height = 16 - - box.grandchild1_2 = Node(style=Style()) - - box.child1 = Node(style=Style(), children=[box.grandchild1_1, box.grandchild1_2]) - box.child1.layout.min_content_width = 5 - box.child1.layout.content_width = 10 - box.child1.layout.min_content_height = 8 - box.child1.layout.content_height = 16 - box.child2 = Node(style=Style(), children=[]) - - box.node = Node(style=Style(), children=[box.child1, box.child2]) - box.node.layout.min_content_width = 5 - box.node.layout.content_width = 10 - box.node.layout.min_content_height = 8 - box.node.layout.content_height = 16 - - return box - - -def assert_layout(box, expected): - actual = { - "origin": (box._origin_left, box._origin_top), - "min_size": (box.min_width, box.min_height), - "size": (box.width, box.height), - "content": (box.content_width, box.content_height), - "relative": ( - box.content_top, - box.content_right, - box.content_bottom, - box.content_left, - ), - "absolute": ( - box.absolute_content_top, - box.absolute_content_right, - box.absolute_content_bottom, - box.absolute_content_left, - ), - } - assert actual == expected - - -def test_repr(box): - box.node.layout._origin_top = 1 - box.node.layout._origin_left = 2 - assert repr(box.node.layout) == "" - - -def test_initial(box): - # Core attributes have been stored - assert_layout( - box.node.layout, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (10, 16), - "content": (10, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 16, 0), - }, - ) - - -@pytest.mark.parametrize( - "dimension, val1, expected1, val2, expected2", - [ - ( - "content_top", - 5, - { - "origin": (0, 0), - "min_size": (5, 13), - "size": (10, 21), - "content": (10, 16), - "relative": (5, 0, 0, 0), - "absolute": (5, 10, 21, 0), - }, - 7, - { - "origin": (0, 0), - "min_size": (5, 15), - "size": (10, 23), - "content": (10, 16), - "relative": (7, 0, 0, 0), - "absolute": (7, 10, 23, 0), - }, - ), - ( - "content_left", - 5, - { - "origin": (0, 0), - "min_size": (10, 8), - "size": (15, 16), - "content": (10, 16), - "relative": (0, 0, 0, 5), - "absolute": (0, 15, 16, 5), - }, - 7, - { - "origin": (0, 0), - "min_size": (12, 8), - "size": (17, 16), - "content": (10, 16), - "relative": (0, 0, 0, 7), - "absolute": (0, 17, 16, 7), - }, - ), - ( - "min_content_width", - 8, - { - "origin": (0, 0), - "min_size": (8, 8), - "size": (10, 16), - "content": (10, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 16, 0), - }, - 9, - { - "origin": (0, 0), - "min_size": (9, 8), - "size": (10, 16), - "content": (10, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 16, 0), - }, - ), - ( - "content_width", - 5, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (5, 16), - "content": (5, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 5, 16, 0), - }, - 7, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (7, 16), - "content": (7, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 7, 16, 0), - }, - ), - ( - "min_content_height", - 7, - { - "origin": (0, 0), - "min_size": (5, 7), - "size": (10, 16), - "content": (10, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 16, 0), - }, - 8, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (10, 16), - "content": (10, 16), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 16, 0), - }, - ), - ( - "content_height", - 10, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (10, 10), - "content": (10, 10), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 10, 0), - }, - 12, - { - "origin": (0, 0), - "min_size": (5, 8), - "size": (10, 12), - "content": (10, 12), - "relative": (0, 0, 0, 0), - "absolute": (0, 10, 12, 0), - }, - ), - ], -) -def test_set_content_dimension(box, dimension, val1, expected1, val2, expected2): - setattr(box.node.layout, dimension, val1) - assert_layout(box.node.layout, expected1) - - # Set to a new value - setattr(box.node.layout, dimension, val2) - assert_layout(box.node.layout, expected2) - - -def test_descendent_offsets(box): - box.node.layout.content_top = 7 - box.node.layout.content_left = 8 - - box.child1.layout.content_top = 9 - box.child1.layout.content_left = 10 - - box.grandchild1_1.layout.content_top = 11 - box.grandchild1_1.layout.content_left = 12 - - assert_layout( - box.node.layout, - { - "origin": (0, 0), - "min_size": (13, 15), - "size": (18, 23), - "content": (10, 16), - "relative": (7, 0, 0, 8), - "absolute": (7, 18, 23, 8), - }, - ) - - assert_layout( - box.child1.layout, - { - "origin": (8, 7), - "min_size": (15, 17), - "size": (20, 25), - "content": (10, 16), - "relative": (9, 0, 0, 10), - "absolute": (16, 28, 32, 18), - }, - ) - - assert_layout( - box.grandchild1_1.layout, - { - "origin": (18, 16), - "min_size": (17, 19), - "size": (22, 27), - "content": (10, 16), - "relative": (11, 0, 0, 12), - "absolute": (27, 40, 43, 30), - }, - ) - - # Modify the grandchild position - box.grandchild1_1.layout.content_top = 13 - box.grandchild1_1.layout.content_left = 14 - - # Only the grandchild position has changed. - assert_layout( - box.node.layout, - { - "origin": (0, 0), - "min_size": (13, 15), - "size": (18, 23), - "content": (10, 16), - "relative": (7, 0, 0, 8), - "absolute": (7, 18, 23, 8), - }, - ) - - assert_layout( - box.child1.layout, - { - "origin": (8, 7), - "min_size": (15, 17), - "size": (20, 25), - "content": (10, 16), - "relative": (9, 0, 0, 10), - "absolute": (16, 28, 32, 18), - }, - ) - - assert_layout( - box.grandchild1_1.layout, - { - "origin": (18, 16), - "min_size": (19, 21), - "size": (24, 29), - "content": (10, 16), - "relative": (13, 0, 0, 14), - "absolute": (29, 42, 45, 32), - }, - ) - - # Modify the child position - box.child1.layout.content_top = 15 - box.child1.layout.content_left = 16 - - # The child and grandchild positions have changed. - assert_layout( - box.node.layout, - { - "origin": (0, 0), - "min_size": (13, 15), - "size": (18, 23), - "content": (10, 16), - "relative": (7, 0, 0, 8), - "absolute": (7, 18, 23, 8), - }, - ) - - assert_layout( - box.child1.layout, - { - "origin": (8, 7), - "min_size": (21, 23), - "size": (26, 31), - "content": (10, 16), - "relative": (15, 0, 0, 16), - "absolute": (22, 34, 38, 24), - }, - ) - - assert_layout( - box.grandchild1_1.layout, - { - "origin": (24, 22), - "min_size": (19, 21), - "size": (24, 29), - "content": (10, 16), - "relative": (13, 0, 0, 14), - "absolute": (35, 48, 51, 38), - }, - ) - - -def test_absolute_equalities(box): - # Move the box around and set some borders. - layout = box.node.layout - - layout.origin_top = 100 - layout.origin_left = 200 - - layout.content_top = 50 - layout.content_left = 75 - layout.content_right = 42 - layout.content_bottom = 37 - - assert ( - layout.absolute_content_left + layout.content_width - == layout.absolute_content_right - ) - assert ( - layout.absolute_content_top + layout.content_height - == layout.absolute_content_bottom - ) - - assert ( - layout.content_left + layout.content_width + layout.content_right - == layout.width - ) - assert ( - layout.content_top + layout.content_height + layout.content_bottom - == layout.height - ) diff --git a/tests/test_node.py b/tests/test_node.py deleted file mode 100644 index be8233e..0000000 --- a/tests/test_node.py +++ /dev/null @@ -1,467 +0,0 @@ -from unittest.mock import Mock -from warnings import catch_warnings, filterwarnings - -import pytest - -from tests.utils import mock_attr, prep_style_class -from travertino.declaration import BaseStyle, Choices, validated_property -from travertino.layout import BaseBox, Viewport -from travertino.node import Node -from travertino.size import BaseIntrinsicSize - - -@prep_style_class -@mock_attr("reapply") -class Style(BaseStyle): - int_prop: int = validated_property(Choices(integer=True)) - - class IntrinsicSize(BaseIntrinsicSize): - pass - - class Box(BaseBox): - pass - - def layout(self, viewport): - # A simple layout scheme that allocates twice the viewport size. - self._applicator.node.layout.content_width = viewport.width * 2 - self._applicator.node.layout.content_height = viewport.height * 2 - - -@prep_style_class -class OldStyle(Style): - # Uses two-argument layout(), as in Toga <= 0.4.8 - def layout(self, node, viewport): - # A simple layout scheme that allocates twice the viewport size. - super().layout(viewport) - - -@prep_style_class -class TypeErrorStyle(Style): - # Uses the correct signature, but raises an unrelated TypeError in layout - def layout(self, viewport): - raise TypeError("An unrelated TypeError has occurred somewhere in layout()") - - -@prep_style_class -class OldTypeErrorStyle(Style): - # Just to be extra safe... - def layout(self, node, viewport): - raise TypeError("An unrelated TypeError has occurred somewhere in layout()") - - -@prep_style_class -class BrokenStyle(BaseStyle): - def reapply(self): - raise AttributeError("Missing attribute, node not ready for style application") - - class IntrinsicSize(BaseIntrinsicSize): - pass - - class Box(BaseBox): - pass - - def layout(self, viewport): - # A simple layout scheme that allocates twice the viewport size. - self._applicator.node.layout.content_width = viewport.width * 2 - self._applicator.node.layout.content_height = viewport.height * 2 - - -class AttributeTestStyle(BaseStyle): - class IntrinsicSize(BaseIntrinsicSize): - pass - - class Box(BaseBox): - pass - - def reapply(self): - assert self._applicator.node.style is self - - -def test_create_leaf(): - """A leaf can be created""" - style = Style() - leaf = Node(style=style) - - assert leaf._children is None - assert leaf.children == [] - assert not leaf.can_have_children - - # An unattached leaf is a root - assert leaf.parent is None - assert leaf.root == leaf - - # A leaf can't have children - child = Node(style=style) - - with pytest.raises(ValueError): - leaf.add(child) - - -def test_create_node(): - """A node can be created with children""" - style = Style() - - child1 = Node(style=style) - child2 = Node(style=style) - child3 = Node(style=style) - - node = Node(style=style, children=[child1, child2, child3]) - - assert node.children == [child1, child2, child3] - assert node.can_have_children - - # The node is the root as well. - assert node.parent is None - assert node.root == node - - # The children all point at the node. - assert child1.parent == node - assert child1.root == node - - assert child2.parent == node - assert child2.root == node - - assert child3.parent == node - assert child3.root == node - - # Create another node - new_node = Node(style=style, children=[]) - - assert new_node.children == [] - assert new_node.can_have_children - - # Add the old node as a child of the new one. - new_node.add(node) - - # The new node is the root - assert new_node.parent is None - assert new_node.root == new_node - - # The node is the root as well. - assert node.parent == new_node - assert node.root == new_node - - # The children all point at the node. - assert child1.parent == node - assert child1.root == new_node - - assert child2.parent == node - assert child2.root == new_node - - assert child3.parent == node - assert child3.root == new_node - - -@pytest.mark.parametrize("StyleClass", [Style, OldStyle]) -def test_refresh(StyleClass): - """The layout can be refreshed, and the applicator invoked""" - - # Define an applicator that tracks the node being rendered and its size - class Applicator: - def __init__(self, node): - self.tasks = [] - self.node = node - - def set_bounds(self): - self.tasks.append( - ( - self.node, - self.node.layout.content_width, - self.node.layout.content_height, - ) - ) - - class TestNode(Node): - def __init__(self, style, children=None): - super().__init__( - style=style, applicator=Applicator(self), children=children - ) - - # Define a simple 2 level tree of nodes. - style = StyleClass() - child1 = TestNode(style=style) - child2 = TestNode(style=style) - child3 = TestNode(style=style) - - node = TestNode(style=style, children=[child1, child2, child3]) - - # Refresh the root node - node.refresh(Viewport(width=10, height=20)) - - # Check the output is as expected - assert node.applicator.tasks == [(node, 20, 40)] - assert child1.applicator.tasks == [] - assert child2.applicator.tasks == [] - assert child3.applicator.tasks == [] - - # Reset the applicator - node.applicator.tasks = [] - - # Refresh a child node - child1.refresh(Viewport(width=15, height=25)) - - # The root node was rendered, not the child. - assert node.applicator.tasks == [(node, 30, 50)] - assert child1.applicator.tasks == [] - assert child2.applicator.tasks == [] - assert child3.applicator.tasks == [] - - -@pytest.mark.parametrize("StyleClass", [TypeErrorStyle, OldTypeErrorStyle]) -def test_type_error_in_layout(StyleClass): - """The shim shouldn't hide unrelated TypeErrors.""" - - class Applicator: - def set_bounds(self): - pass - - node = Node(style=StyleClass(), applicator=Applicator()) - with pytest.raises(TypeError, match=r"unrelated TypeError"): - node.refresh(Viewport(50, 50)) - - -def test_add(): - """Nodes can be added as children to another node""" - - style = Style() - node = Node(style=style, children=[]) - - child = Node(style=style) - node.add(child) - - assert child in node.children - assert child.parent == node - assert child.root == node.root - - -def test_insert(): - """Node can be inserted at a specific position as a child""" - - style = Style() - child1 = Node(style=style) - child2 = Node(style=style) - child3 = Node(style=style) - node = Node(style=style, children=[child1, child2, child3]) - - child4 = Node(style=style) - - index = 2 - node.insert(index, child4) - - assert child4 in node.children - assert child4.parent == node - assert child4.root == node.root - - assert node.children.index(child4) == index - - -def test_remove(): - """Children can be removed from node""" - - style = Style() - child1 = Node(style=style) - child2 = Node(style=style) - child3 = Node(style=style) - node = Node(style=style, children=[child1, child2, child3]) - - node.remove(child1) - - assert child1 not in node.children - assert child1.parent is None - assert child1.root == child1 - - -def test_clear(): - """Node can be inserted at a specific position as a child""" - style = Style() - children = [Node(style=style), Node(style=style), Node(style=style)] - node = Node(style=style, children=children) - - for child in children: - assert child in node.children - assert child.parent == node - assert child.root == node - assert node.children == children - - node.clear() - - for child in children: - assert child not in node.children - assert child.parent is None - assert child.root == child - - assert node.children == [] - - -def test_create_with_no_applicator(): - """A node can be created without an applicator.""" - style = Style(int_prop=5) - node = Node(style=style) - - # Style copies on assignment. - assert isinstance(node.style, Style) - assert node.style == style - assert node.style is not style - - # Since no applicator has been assigned, style wasn't applied. - node.style.reapply.assert_not_called() - - -def test_create_with_applicator(): - """A node can be created with an applicator.""" - style = Style(int_prop=5) - applicator = Mock() - node = Node(style=style, applicator=applicator) - - # Style copies on assignment. - assert isinstance(node.style, Style) - assert node.style == style - assert node.style is not style - - # Applicator assignment does *not* copy. - assert node.applicator is applicator - # Applicator gets a reference back to its node and to the style. - assert applicator.node is node - assert node.style._applicator is applicator - - # Assigning a non-None applicator should always apply style. - node.style.reapply.assert_called_once() - - -@pytest.mark.parametrize( - "node", - [ - Node(style=Style()), - Node(style=Style(), applicator=Mock()), - ], -) -def test_assign_applicator(node): - """A node can be assigned an applicator after creation.""" - node.style.reapply.reset_mock() - - applicator = Mock() - node.applicator = applicator - - # Applicator assignment does *not* copy. - assert node.applicator is applicator - # Applicator gets a reference back to its node and to the style. - assert applicator.node is node - assert node.style._applicator is applicator - - # Assigning a non-None applicator should always apply style. - node.style.reapply.assert_called_once() - - -@pytest.mark.parametrize( - "node", - [ - Node(style=Style()), - Node(style=Style(), applicator=Mock()), - ], -) -def test_assign_applicator_none(node): - """A node can have its applicator set to None.""" - node.style.reapply.reset_mock() - - node.applicator = None - assert node.applicator is None - - # Should be updated on style as well - assert node.style._applicator is None - # Assigning None to applicator does not trigger reapply. - node.style.reapply.assert_not_called() - - -def assign_new_applicator(): - """Assigning a new applicator clears reference to node on the old applicator.""" - applicator_1 = Mock() - node = Node(style=Style(), applicator=applicator_1) - - assert applicator_1.node is node - - applicator_2 = Mock() - node.applicator = applicator_2 - - assert applicator_1.node is None - assert applicator_2.node is node - - -def assign_new_applicator_none(): - """Assigning None to applicator clears reference to node on the old applicator.""" - applicator = Mock() - node = Node(style=Style(), applicator=applicator) - - assert applicator.node is node - - node.applicator = None - - assert applicator.node is None - - -def test_assign_style_with_applicator(): - """Assigning a new style triggers a reapply if an applicator is already present.""" - style_1 = Style(int_prop=5) - node = Node(style=style_1, applicator=Mock()) - - node.style.reapply.reset_mock() - style_2 = Style(int_prop=10) - node.style = style_2 - - # Style copies on assignment. - assert isinstance(node.style, Style) - assert node.style == style_2 - assert node.style is not style_2 - - assert node.style != style_1 - - # Since an applicator has already been assigned, assigning style applies the style. - node.style.reapply.assert_called_once() - - -def test_assign_style_with_no_applicator(): - """Assigning a new style doesn't trigger a reapply if an applicator isn't present.""" - style_1 = Style(int_prop=5) - node = Node(style=style_1) - - node.style.reapply.reset_mock() - style_2 = Style(int_prop=10) - node.style = style_2 - - # Style copies on assignment. - assert isinstance(node.style, Style) - assert node.style == style_2 - assert node.style is not style_2 - - assert node.style != style_1 - - # Since no applicator was present, style should not be applied. - node.style.reapply.assert_not_called() - - -def test_apply_before_node_is_ready(): - """Triggering a reapply raises a warning if the node is not ready to apply style.""" - style = BrokenStyle() - applicator = Mock() - - with pytest.warns(RuntimeWarning): - node = Node(style=style) - node.applicator = applicator - - with pytest.warns(RuntimeWarning): - node.style = BrokenStyle() - - with pytest.warns(RuntimeWarning): - Node(style=style, applicator=applicator) - - -def test_applicator_has_node_reference(): - """Applicator should have a reference to its node before style is first applied.""" - - # We can't just check it after creating the widget, because at that point the - # reapply will have already happened. AttributeTestStyle has a reapply() method - # that asserts the reference trail of style -> applicator -> node -> style is - # already intact at the point that reapply is called. - - with catch_warnings(): - filterwarnings("error", category=RuntimeWarning) - Node(style=AttributeTestStyle(), applicator=Mock()) diff --git a/tests/test_size.py b/tests/test_size.py deleted file mode 100644 index ebe4cf2..0000000 --- a/tests/test_size.py +++ /dev/null @@ -1,138 +0,0 @@ -from typing import NamedTuple -from unittest.mock import Mock - -import pytest - -from travertino.size import BaseIntrinsicSize, at_least - - -class Size(NamedTuple): - width: int - height: int - ratio: float - - def change(self, dimension, value): - return self._replace(**{dimension: value}) - - -BASE_SIZE = Size(width=1, height=2, ratio=0.1) - - -class TestBox: - pass - - -@pytest.fixture -def box(): - box = TestBox() - - box.maxDiff = None - - box.layout = Mock() - box.size = BaseIntrinsicSize(layout=box.layout) - box.size._width, box.size._height, box.size._ratio = BASE_SIZE - - assert_size(box.size, BASE_SIZE) - - return box - - -def assert_size(size, values): - assert (size.width, size.height, size.ratio) == values - - -def test_at_least_repr(): - assert repr(at_least(10)) == "at least 10" - - -def test_size_repr(box): - assert repr(box.size) == "(1, 2)" - box.size.width = at_least(10) - assert repr(box.size) == "(at least 10, 2)" - - -@pytest.mark.parametrize("dimension", ["width", "height"]) -def test_set_dimension(box, dimension): - setattr(box.size, dimension, 10) - assert_size(box.size, BASE_SIZE.change(dimension, 10)) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(**{f"intrinsic_{dimension}": 10}) - - # Clean the layout - box.layout.dirty.reset_mock() - - # Set the width to the same value - setattr(box.size, dimension, 10) - assert_size(box.size, BASE_SIZE.change(dimension, 10)) - - # Layout has NOT been dirtied. - box.layout.dirty.assert_not_called() - - # Set the width to something new - setattr(box.size, dimension, 20) - assert_size(box.size, BASE_SIZE.change(dimension, 20)) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(**{f"intrinsic_{dimension}": 20}) - - -@pytest.mark.parametrize("dimension", ["width", "height"]) -def test_set_dimension_at_least(box, dimension): - setattr(box.size, dimension, at_least(10)) - assert_size(box.size, BASE_SIZE.change(dimension, at_least(10))) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(**{f"intrinsic_{dimension}": at_least(10)}) - - # Clean the layout - box.layout.dirty.reset_mock() - - # Set the width to the same value - setattr(box.size, dimension, at_least(10)) - assert_size(box.size, BASE_SIZE.change(dimension, at_least(10))) - - # Layout has NOT been dirtied. - box.layout.dirty.assert_not_called() - - # Set the width to the same value, but not as a minimum - setattr(box.size, dimension, 10) - assert_size(box.size, BASE_SIZE.change(dimension, 10)) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(**{f"intrinsic_{dimension}": 10}) - - # Clean the layout - box.layout.dirty.reset_mock() - - # Set the width to something new - setattr(box.size, dimension, at_least(20)) - assert_size(box.size, BASE_SIZE.change(dimension, at_least(20))) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(**{f"intrinsic_{dimension}": at_least(20)}) - - -def test_set_ratio(box): - box.size.ratio = 0.5 - assert_size(box.size, (1, 2, 0.5)) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(intrinsic_ratio=0.5) - - # Clean the layout - box.layout.dirty.reset_mock() - - # Set the ratio to the same value - box.size.ratio = 0.5 - assert_size(box.size, (1, 2, 0.5)) - - # Layout has NOT been dirtied. - box.layout.dirty.assert_not_called() - - # Set the ratio to something else - box.size.ratio = 0.75 - assert_size(box.size, (1, 2, 0.75)) - - # Layout has been dirtied. - box.layout.dirty.assert_called_once_with(intrinsic_ratio=0.75) diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index 0649494..0000000 --- a/tests/utils.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -from dataclasses import dataclass -from unittest.mock import Mock - -if sys.version_info < (3, 10): - _DATACLASS_KWARGS = {"init": False} -else: - _DATACLASS_KWARGS = {"kw_only": True} - - -def prep_style_class(cls): - """Decorator to apply dataclass and mock apply.""" - return mock_attr("apply")(dataclass(**_DATACLASS_KWARGS)(cls)) - - -def mock_attr(attr): - """Mock an arbitrary attribute of a class.""" - - def returned_decorator(cls): - orig_init = cls.__init__ - - def __init__(self, *args, **kwargs): - setattr(self, attr, Mock()) - orig_init(self, *args, **kwargs) - - cls.__init__ = __init__ - return cls - - return returned_decorator diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c18af7c..0000000 --- a/tox.ini +++ /dev/null @@ -1,41 +0,0 @@ -# Flake8 doesn't believe in pyproject.toml, so we put the configuration here. -[flake8] -exclude= - local/*,\ - docs/*,\ - build/*,\ - dist/*,\ - .tox/*, \ - venv* -max-complexity = 25 -max-line-length = 119 -ignore = - # line break occurred before a binary operator - W503, - -[tox] -envlist = towncrier-check,pre-commit,py{39,310,311,312,313,314} -skip_missing_interpreters = true - -[testenv:pre-commit] -package = wheel -wheel_build_env = .pkg -extras = dev -commands = pre-commit run --all-files --show-diff-on-failure --color=always - -[testenv:py{,39,310,311,312,313,314}] -package = wheel -wheel_build_env = .pkg -depends = - towncrier-check - pre-commit -extras = dev -commands = python -m pytest {posargs:-vv --color yes} - -[testenv:towncrier{,-check}] -skip_install = True -deps = - towncrier==24.8.0 -commands = - check : python -m towncrier.check --compare-with origin/main - !check : python -m towncrier {posargs} From 1892c2d9ec81826059ab8d2d2f6044d843d9b6e7 Mon Sep 17 00:00:00 2001 From: Charles Whittington Date: Sun, 19 Jan 2025 16:36:29 -0500 Subject: [PATCH 4/4] Removed nonexistant logo --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 2395053..bf7a99d 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,3 @@ -|logo| - Travertino ==========