pax_global_header00006660000000000000000000000064150073610770014520gustar00rootroot0000000000000052 comment=8f1cb81ddd968fccc86b0b9c35c1a656cb7f1dc0 django-rich-2.0.0/000077500000000000000000000000001500736107700137045ustar00rootroot00000000000000django-rich-2.0.0/.editorconfig000066400000000000000000000003451500736107700163630ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.py] indent_size = 4 [Makefile] indent_style = tab django-rich-2.0.0/.github/000077500000000000000000000000001500736107700152445ustar00rootroot00000000000000django-rich-2.0.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000001311500736107700200360ustar00rootroot00000000000000This project follows [Django's Code of Conduct](https://www.djangoproject.com/conduct/). django-rich-2.0.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001500736107700174275ustar00rootroot00000000000000django-rich-2.0.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000000341500736107700214140ustar00rootroot00000000000000blank_issues_enabled: false django-rich-2.0.0/.github/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000004111500736107700232670ustar00rootroot00000000000000name: Feature Request description: Request an enhancement or new feature. body: - type: textarea id: description attributes: label: Description description: Please describe your feature request with appropriate detail. validations: required: true django-rich-2.0.0/.github/ISSUE_TEMPLATE/issue.yml000066400000000000000000000015261500736107700213060ustar00rootroot00000000000000name: Issue description: File an issue body: - type: input id: python_version attributes: label: Python Version description: Which version of Python were you using? placeholder: 3.9.0 validations: required: false - type: input id: django_version attributes: label: Django Version description: Which version of Django were you using? placeholder: 3.2.0 validations: required: false - type: input id: package_version attributes: label: Package Version description: Which version of this package were you using? If not the latest version, please check this issue has not since been resolved. placeholder: 1.0.0 validations: required: false - type: textarea id: description attributes: label: Description description: Please describe your issue. validations: required: true django-rich-2.0.0/.github/SECURITY.md000066400000000000000000000001011500736107700170250ustar00rootroot00000000000000Please report security issues directly over email to me@adamj.eu django-rich-2.0.0/.github/dependabot.yml000066400000000000000000000002471500736107700200770ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: "/" groups: "GitHub Actions": patterns: - "*" schedule: interval: monthly django-rich-2.0.0/.github/workflows/000077500000000000000000000000001500736107700173015ustar00rootroot00000000000000django-rich-2.0.0/.github/workflows/main.yml000066400000000000000000000050621500736107700207530ustar00rootroot00000000000000name: CI on: push: branches: - main tags: - '**' pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-24.04 strategy: matrix: python-version: - 3.9 - '3.10' - '3.11' - '3.12' - '3.13' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install uv uses: astral-sh/setup-uv@v6 with: enable-cache: true - name: Install dependencies run: uv pip install --system tox tox-uv - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ matrix.python-version }} path: '${{ github.workspace }}/.coverage.*' include-hidden-files: true if-no-files-found: error coverage: name: Coverage runs-on: ubuntu-24.04 needs: tests steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.13' - name: Install uv uses: astral-sh/setup-uv@v6 - name: Install dependencies run: uv pip install --system coverage[toml] - name: Download data uses: actions/download-artifact@v4 with: path: ${{ github.workspace }} pattern: coverage-data-* merge-multiple: true - name: Combine coverage and fail if it's <100% run: | python -m coverage combine python -m coverage html --skip-covered --skip-empty python -m coverage report --fail-under=100 echo "## Coverage summary" >> $GITHUB_STEP_SUMMARY python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - name: Upload HTML report if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: html-report path: htmlcov release: needs: [coverage] if: success() && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-24.04 environment: release permissions: contents: read id-token: write steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v6 - name: Build run: uv build - uses: pypa/gh-action-pypi-publish@release/v1 django-rich-2.0.0/.gitignore000066400000000000000000000000771500736107700157000ustar00rootroot00000000000000*.egg-info/ *.pyc /.coverage /.coverage.* /.tox /build/ /dist/ django-rich-2.0.0/.pre-commit-config.yaml000066400000000000000000000031741500736107700201720ustar00rootroot00000000000000ci: autoupdate_schedule: monthly default_language_version: python: python3.13 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-added-large-files - id: check-case-conflict - id: check-json - id: check-merge-conflict - id: check-symlinks - id: check-toml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/tox-dev/pyproject-fmt rev: v2.5.1 hooks: - id: pyproject-fmt - repo: https://github.com/tox-dev/tox-ini-fmt rev: 1.5.0 hooks: - id: tox-ini-fmt - repo: https://github.com/rstcheck/rstcheck rev: v6.2.4 hooks: - id: rstcheck additional_dependencies: - tomli==2.0.1 - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/adamchainz/django-upgrade rev: 1.24.0 hooks: - id: django-upgrade args: [--target-version, '4.2'] - repo: https://github.com/psf/black-pre-commit-mirror rev: 25.1.0 hooks: - id: black - repo: https://github.com/adamchainz/blacken-docs rev: 1.19.1 hooks: - id: blacken-docs additional_dependencies: - black==25.1.0 - repo: https://github.com/pycqa/isort rev: 6.0.1 hooks: - id: isort name: isort (python) - repo: https://github.com/PyCQA/flake8 rev: 7.2.0 hooks: - id: flake8 additional_dependencies: - flake8-bugbear - flake8-comprehensions - flake8-logging - flake8-tidy-imports - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.15.0 hooks: - id: mypy additional_dependencies: - django-stubs==5.1.2 - rich - types-python-dateutil django-rich-2.0.0/CHANGELOG.rst000066400000000000000000000053621500736107700157330ustar00rootroot00000000000000========= Changelog ========= 2.0.0 (2025-05-09) ------------------ * Change ``make_rich_console()`` from a ``partial()`` into a method, to avoid this warning on Python 3.13: ``FutureWarning``: ``functools.partial`` will be a method descriptor in future Python versions; wrap it in ``staticmethod()`` if you want to preserve the old behavior If you have defined ``make_rich_console`` as a ``partial()`` in your own code, you will need to change it to a method or wrap it with ``staticmethod()``. 1.14.0 (2025-02-06) ------------------- * Support Django 5.2. 1.13.0 (2024-10-29) ------------------- * Drop Django 3.2 to 4.1 support. 1.12.0 (2024-10-23) ------------------- * Improve the type hint for ``RichCommand.make_rich_console`` to support overriding. * Drop Python 3.8 support. * Support Python 3.13. 1.11.0 (2024-08-30) ------------------- * Extend test runner to style subtests, teardown errors, and teardown failures. THanks to David Smith in `PR #224 `__. * Use unittest’s traceback cleaning. Unnecessary frames will now be hidden, as with vanilla unittest, and ``--pdb`` will skip past them. `PR #225 `__. 1.10.0 (2024-08-12) ------------------- * Add a ``shell`` command extension that enables Rich’s pretty-printing. Activate this feature by adding ``django_rich`` to your ``INSTALLED_APPS`` setting. Thanks to q0w in `PR #78 `__. * Extend test runner to display test durations from |--durations|__ in a Rich table. .. |--durations| replace:: ``--durations`` __ https://docs.djangoproject.com/en/stable/ref/django-admin/#cmdoption-test-durations `PR #213 `__. * Make test runner use Rich rules between output sections. `PR #216 `__. 1.9.0 (2024-06-19) ------------------ * Support Django 5.1. 1.8.0 (2023-10-11) ------------------ * Support Django 5.0. 1.7.0 (2023-07-10) ------------------ * Drop Python 3.7 support. 1.6.0 (2023-06-14) ------------------ * Support Python 3.12. 1.5.0 (2023-02-25) ------------------ * Support Django 4.2. 1.4.0 (2022-06-05) ------------------ * Support Python 3.11. * Support Django 4.1. 1.3.0 (2022-05-10) ------------------ * Drop support for Django 2.2, 3.0, and 3.1. 1.2.0 (2022-02-25) ------------------ * Add ``django_rich.test.RichRunner``, a custom test runner that uses Rich colouring and tracebacks. Thanks to David Smith in `PR #29 `__. 1.1.0 (2022-01-10) ------------------ * Drop Python 3.6 support. 1.0.0 (2021-11-25) ------------------ * First version. django-rich-2.0.0/HISTORY.rst000066400000000000000000000001061500736107700155740ustar00rootroot00000000000000See https://github.com/adamchainz/django-rich/blob/main/CHANGELOG.rst django-rich-2.0.0/LICENSE000066400000000000000000000020551500736107700147130ustar00rootroot00000000000000MIT License Copyright (c) 2021 Adam Johnson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. django-rich-2.0.0/MANIFEST.in000066400000000000000000000002051500736107700154370ustar00rootroot00000000000000prune tests include CHANGELOG.rst include img/*.png include LICENSE include pyproject.toml include README.rst include src/*/py.typed django-rich-2.0.0/README.rst000066400000000000000000000143431500736107700154000ustar00rootroot00000000000000=========== django-rich =========== .. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-rich/main.yml.svg?branch=main&style=for-the-badge :target: https://github.com/adamchainz/django-rich/actions?workflow=CI .. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge :target: https://github.com/adamchainz/django-rich/actions?workflow=CI .. image:: https://img.shields.io/pypi/v/django-rich.svg?style=for-the-badge :target: https://pypi.org/project/django-rich/ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge :target: https://github.com/psf/black .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge :target: https://github.com/pre-commit/pre-commit :alt: pre-commit Extensions for using `Rich `__ with Django. ---- **Work smarter and faster** with my book `Boost Your Django DX `__ which covers many ways to improve your development experience. I wrote django-rich whilst working on the book! ---- Requirements ------------ Python 3.9 to 3.13 supported. Django 4.2 to 5.2 supported. Installation ------------ 1. Install with **pip**: .. code-block:: sh python -m pip install django-rich None of django-rich’s features are activated by default. Follow the documentation below to use them. Reference --------- ``shell`` command integration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ django-rich has an extended version of Django’s built-in |shell command|__ that enables `Rich’s pretty-printing `__. To activate this feature, add ``django_rich`` to your ``INSTALLED_APPS`` setting: .. |shell command| replace:: ``shell`` command __ https://docs.djangoproject.com/en/stable/ref/django-admin/#shell .. code-block:: python INSTALLED_APPS = [ ..., "django_rich", ..., ] This feature only affects the Python and bypthon interpreters, not IPython. For IPython support, see `the Rich documentation `__. ``django_rich.management.RichCommand`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A subclass of Django’s |BaseCommand|__ class that sets its ``self.console`` to a Rich |Console|__. The ``Console`` uses the command’s ``stdout`` argument, which defaults to ``sys.stdout``. Colourization is enabled or disabled according to Django’s ``--no-color`` and ``--force-color`` flags. .. |BaseCommand| replace:: ``BaseCommand`` __ https://docs.djangoproject.com/en/stable/howto/custom-management-commands/#django.core.management.BaseCommand .. |Console| replace:: ``Console`` __ https://rich.readthedocs.io/en/stable/console.html Use the features of ``self.console`` as you like: .. code-block:: python from time import sleep from django_rich.management import RichCommand class Command(RichCommand): def handle(self, *args, **options): self.console.print("[bold blue]Frobnicating widgets:[/bold blue]") with self.console.status("Starting...") as status: for i in range(1, 11): status.update(f"Widget {i}...") sleep(1) self.console.log(f"Widget {i} frobnicated.") You can customize the construction of the ``Console`` by overriding the ``make_rich_console()`` method. This takes some keyword arguments and passes them to the ``Console`` constructor. For example, to disable the on-by-default ``markup`` and ``highlighting`` flags, add them in a call to ``super()``: .. code-block:: python from functools import partial from django_rich.management import RichCommand from rich.console import Console class Command(RichCommand): def make_rich_console(self, **kwargs): return super().make_rich_console(**kwargs, markup=False, highlight=False) def handle(self, *args, **options): ... ``django_rich.test.RichRunner`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A subclass of Django's |DiscoverRunner|__ with colourized outputs and `nice traceback rendering `__. .. image:: https://raw.githubusercontent.com/adamchainz/django-rich/main/img/RichRunner.png .. |DiscoverRunner| replace:: ``DiscoverRunner`` __ https://docs.djangoproject.com/en/stable/topics/testing/advanced/#defining-a-test-runner To use this class, point your |TEST_RUNNER|__ setting to it: .. |TEST_RUNNER| replace:: ``TEST_RUNNER`` __ https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-TEST_RUNNER .. code-block:: python TEST_RUNNER = "django_rich.test.RichRunner" You can also use it as a base for further customization. Since only output is modified, it should combine well with other classes. The test runner provides the following features: * Output is colourized wherever possible. This includes Rich’s default `highlighting `__ which will format numbers, quoted strings, URL’s, and more. * Failures and errors use Rich’s `traceback rendering `__. This displays the source code and local values per frame. Each frame also shows the filename and line number, and on many terminals you can click the link to jump to the file at that position. * Output is also colourized when using the ``--debug-sql`` and ``--pdb`` flags. * All other flags from Django's DiscoverRunner continue to work in the normal way. Output Width on CI ~~~~~~~~~~~~~~~~~~ When tests run on your CI system, you might find the output a bit narrow for showing tracebacks correctly. This is because Rich tries to autodetect the terminal dimensions, and if that fails, it will default to 80 characters wide. You can override this default with the ``COLUMNS`` environment variable (as per Python’s |shutil.get_terminal_size() function|__): .. |shutil.get_terminal_size() function| replace:: ``shutil.get_terminal_size()`` function __ https://docs.python.org/3/library/shutil.html#shutil.get_terminal_size .. code-block:: console $ COLUMNS=120 ./manage.py test django-rich-2.0.0/img/000077500000000000000000000000001500736107700144605ustar00rootroot00000000000000django-rich-2.0.0/img/RichRunner.png000066400000000000000000001444411500736107700172550ustar00rootroot00000000000000PNG  IHDR.> pHYs+IDATx_SYR9INBYd ""VEĖ@XKF&魽v񶷷O[mJI!:?a33̜9T T C8B=0װ[nCULl]8_J' b/2\` :H$'E$~99eg 4FTCGؓra>Lf.qoL x4Uoa>~1dJ t@x8KŸʼ"# Wb0 *JHvf%;osr?Y"}0cL/y<+S#FnՑ!/Khj[^kt{ &3"bκ/"U,99l6gee=a:o݈ҿ$mŴݸa1Ѱi{z2O};䧟4˖`27߄ܽv-crR.#ñmddBm~;5ZW'ѣCP ݼoK[f͓n;ϑaRV|&|ON kU1![?R/U,y_T=$^v,v{'*&:Q1B199h| ͏HNN&"D#G\vm``>880,_=Cn葇sɷB#[hH sGI'0 xqSSUU2j5֞;7=o^쩮 XokJCz99_rօ ?{W38'q|epJ/^ @`V'X|=o:3n:_߽xb^D!?erXU쫯Y30H2>|v-_~& lr2 U8YbqGGX/Ms] {W֭`S{2Ynq-BQ,Vm\{w9?;$tE |oŷ\3NYү8)zwH{r4r[n[n0dTkNy=!{Qym3eWq8abۼfv1M_t9c|>G733+..NԘL&U, CssJqAVb*fMgT󝟿gלV^,Y36I'^\/>qpܰ-7oD*|S1cP^h*,,(=`C}}}fsu竬tuuU&<<<00СC 5_rnNҏtjcjB!}д{B߂:~jðhThu{T3;^d1-} *vrj*4 sccӣ,+[u&ߦ&*6:kyǧTEcQ6y^wh0ςB,87oynAqEqzw]K޽n뜩Zu򣏔6*vbj*tp0i?h~EղQ9N=0h0(>*c\  ::CNso G;}lω KZŇ%̭ D_~b*dmèԇdbU|Ҵ|80}ٺWy_Ő)-u=8ӓ95J UUGxCDV1ELViLK!ȃ-#U6q|(}>o)H8 Eb1uz܈^?O?i>4mQŽv-`.1*S7*'|,*6ce"'(3TᄊL&Hddzi{f r>>,0*g1icpVFŘ% +_ ڿ(7l`MODLfܷlcŖh-ʕw<1*MV1L4}ew rb7?t ISkW\ٸq#xll,1AIDjJc*b+8_߻SN'g|i))*֔؄SCRoJ u(,=;}DM*5T vh\d:ዻbccch$ٳ*j 1aDvV݈8w9[-؏?j@7-[Dw{޺:۶NM޽C3JhsFۙ3G+Qbba Y&"wWcMbP6;Ukڐrs%HϘ r/EPՍ~rJ'{}!ԌNƂb|_y';e *F*،Ѩ6*fU gK۟\;;>Id u_慠mTl}zTD DC_]]W ȳd-k~{Olʱcܾm6g l_hQsjKbL]ŽvLO̱vҫe~KpjgK\%+il%ߖN/iD媊566"P.WUlvǭ]>\CTJԓs}u&fwk-Uw~ްaآPi#us@%K~}?:^?x]U͛7FnMl zd*'Ul_~Ч+nD]$UȬmd~}\SC#. bvR&cOdrpj*qtu/ Qw{޿};ʹ|bB7>-9TJb˖Bzk) $*anY֊=@32 Jlyܽ_ 1~'^Ũ3TB^~=}۝461b*+f sbe{do_+f |p_ZObTW{Iye-ԎN/~ʐbe}w-XG#I|:3WU,++f,nWUZ굛;o B'[JJKoհo7Xjchcp^x<|Nd2b~vvJBvuuvDpFPcz{k,:|kQ)=?4uȼl\gI7pUf[?q1;>_T[>""E(FE+8m<}Ūd,m6@-Yŋ :n||[:ڍh}}?tyߍTv%^4pESSurᄈlie+Ywa+31@h A,ɚQh.^%Pf޿#G,ۯ%%MowO}xF#T,9YNJs*$7nZfWb rr޽-;uTl'gddd[[lj4p8ZVVFMp->| gbTO^͕o'ঊb VWU2d2bW 8V\y"hOoRn@b ΕreT `TFdXRH$;vˀgBPPжm|x5/6Tpse뫮&nR66ܹsDHSS>G8wzb' ;;;T0LӧQZtƍv p-]=/rRqubdٌjjj"v@xD9 GawwJg G-Bl8b_~>y/ _T[o游糞_ܩz|=fU~~*VTTv馦&Up}Dbb"YD"X]]]^^Yźl%;;.V+++^&77uʕ+l6݆˖-SǎY_ S]]]UU"gɄ>KRpp9Wϋ4*ݍvz@hj/7T̾@l`0*|޷{Uf< *btE*v?::zx|!@דU,!!… ))) d;vعslh4fJM8zRuu5P(@IKKkmm}pWϋ4*vСݍF[[[ÃB!M:U|ʗ<|>xǥ577T*#G._Vʈ[uٖUVTWWwuuZGәf___ ָ8Ǐ̴y"1\UQDdy=322hkҥht7HN91b555၁Dt]!sx9U ðX?)K 'U^lT-Zj,Ʌ%%%OCŸ\nmmhMM Y'''qYW1΋4*xb45p7Ta)ʀ?%~bh4nݺD9ՕOwX~C/}8j+**"l6Q^"J"nٳgKKKg.TԎQQQޖW 8p-p)`8pOaΝ5 ybׯ_0l͚5OFFO'F(Zj3@*SapLFvz^U1[fC///O>~DDDjj*]*>Uy8q`0H$Oˡ(}b:Gk̏=:cHL̡~]U1bF1-- }FiTӓ\yvWUl?/r8w5P?d9… L&ꪨP˖-3L趍8ZfNep8]]]Cl@l5ëbVUUue˖ݻwllMW.,\:/rbT㰽2˗j ܹst{3GT*bSxNWRRݍ& CTTT```NNؘD"I>>!߽S*_pLF,VT+-߿l6 |rbmmm+VPTUUU===rf8%Uʙe\gb.]R*xQ.TNHt zjzxVSŮM}!oZSSC *׷a裏Zɓ'/]UL*FFF:0LTlZŊÙLfnnnGG]\YPf___b'N FEx<r``ҥK 3XBBL&p8dc2O&v&ޙr:}l62O, ǎmllDӈ4`rʑCރbt*i5NCknnVT84Zt)z36bdf3͖8]o999/_QՇFӋŭAAAyyyB& RRRL&M!Gعshb*6>>>J8 v܉>wi4z+**BJ2D:h'W#G\qF"xllduCst\Ob۶m慄WaŎk/_>::j4+++SSSmV;b逊هϊyrɹv=KOO'Mb&SRRJJJL&˗E:TLi0,;;#ѩXcc#1(.))A!!!Ě-*3iii(wOlvUUdrr *2sd2賗lj*311acZ`VYYY]]mTDDDjj*łn bl8aؖ-[z{{cccjkk6*ϿtRp83J2 HZÉT.;9755=z488XR!g닏t%%%h~}}` H$4`r9ҧ#lʉJގn`*b^6 DL&=‰eT*=:88h2V\9ld27@k艌lkk$F{Ϝ9C Ydځt}}}!!!DM:,%sfJŀ'd###L&SKKL&JP1`߽{7}^yf_**3=[__j~RRҩSS3C"ر9YEOW!::լT^'R1&:}pW y?'տJNNF;Qݮ|I+gҗv|~(--=.Xzzzwwejczzzx<0 JEhT*_tС Jd9sso$\W\zD*+ϕ1 ɔWU۾P(P*ccc˗/' cǎ666 rICl4ZFBa2*++{{{Ϝ9c2fIKK3Lׯ_7 `?~h$0_t*&;:: />>8Sx<^eeK[UyJ8qbhh(?M}RKnTCU~z)?m@oe鰿фS|ҡaʕ###6F1==0'w@/}^zF\W4PVV{}ɹr\.WZY𠠠~P8ߟ>w;\|9&&FV>|(QDbyqׯ\vmHHHYYYoo/J`0?~`PKb 6|>:ͮ.-jҥjuQ`0477T*PiaVfISNPFˏaXff;LG鰿фSRCCJJd loo߸q? _E/U=PKvi4 ྊqњX"<**j||ð B*++1[n%yONN)ׯcf͚ӧO;KJJgQZZn9 l6+J|i!T,77d2w;C{{;i4w܉>jEUPTT•J%Q~tCu#OP:,PFHTѸQT*JhMRV*팊͘M=Pѷ)MC,^|xxliiqo(p壣F255fŮ:<ۡRa4Ս+W:_6+W7KgC.K*F|Pߙv!R;_T*JŨΗ?bΏ͘*jnP1Q1VG?nݺcŊ[\.OII)))1L/_&?GP$lܨ}*VYYPWWfQCb3gUay慠yoiTg$%%utt8T1ҫ،:b.+s?dgg744}$ӳa5G%"p8.///'(m2uCjjj&(rK1AP(ׯ_ƴ4'&(YUy慠566[hہڔQ'BBBf3k:_쐘 sru:lN JUw?TVVVWWUDDDjj*ł.*VUUue˖ݻwllߟmiil!B CTTT```NNؘD"!~t`JU}}}:[ bk֬ .--%/wxq/M:'(333{zzod2byuvvJBO_tITr8bEUzO|T*>Uy慠eee,q܍oT[l퍍 %ΨΗ?;xZ1|? څ@RR١)9EU1@PVV}Fh!իWѳW-v```ll%!!?22 m1tuu 9sbedd\rexxf3 ҤCV1[[[=>R]]mz=ztppd2\Ы}}RKbTCU~WUbj`` }RQ3Lʗ^Au+!hWϗ*_j.\@ҷ#Mr,^:kkkrG:XpjW#,,d2d2 OKгJwj^߼y8Ob**5"2XJ%)lXHd*P1X,޿=KXq|ş?W\BBit*fVӺu 89}#G^^P]X^_Y,TM{ORQ_8E?N_=8 =PAӔ?3F^z?:yw'."TVLIz:%b'ou|g.ȘNLyl/\.LPLUQ0քbLƋ_۶?rX賏Ϣᯃ,Ua&=/\ GڢS[[㿗/_O^^`3 12N|%/VD?O?579=}>M:r덍W?]_II P8l 3S1T T @@P1P1T T IŘ88cxFP1P1T T @@P1`PHJJ:uŋ*T xd/^4ǏxuH$;vxN/UWWҥK1 G?H*΁zJE=֬Yuj: 'Oإ?a0Fq|||ƍI(WMKK3 }}}Ĺ9rڵkht088H---d2]~`0E ᦦ0'X, f3yba?д;+ K('FN#}8UuUŨ򍏏'j 99yz&JJwߙ;ss0V֌ v|810>l6GDDq4bߟ>  rrr._V>p0 +..nmm   4鸚oZZZZZÉ^v-a555၁d0_~pppڵ!!!eee7 V BᮦOTVVfB10hڝFU*eee©O*0_>NP^Mf}=?ՙf>w2x9UlrrrϟQ1899`gT1ooobǽ}ѸuV$Jr&㧥uww?y 5L"hƓ}fL&vQ"T* *3Is/A{{;b3֏Vxy&w_[[k6ibyq]ܼy3}89G,_-LMM69+*LTjadd@Tko||<66֍P+W:s.l?}?P; faQS*_7TabU4ןgXO|gbeYYY@@@ee%Y!psU,==]A=.66b Gxyy{ lxf)l B?T*Bjjj&( bxxx3lNNlbyU'CBBf3 vUOs{>>> bUT׍g[O|3Wm ^ẐYVV҂Tfjd2Y8pr>ѣGU*JJJe!***000''gllL"ФjT쮮.tD@888f͚RbY=U CKKK\\JBj'(333{zzftfԩSh?}|4cos}˖-AAAMMMRTW^|Z1L2vd2b~vvJ"oJU?j2uS=_fނ*F+q)d###6m G[^zupp>00~BȽM18Cddd[[$fuuu 9s*)jkkZZZq5_K%K.^Рlj&W\a î]}/;>Ypommu8WTWWLS>_[[g:jU3̂.S+}8A,#T_RՏf϶Rg[nq^T0 #??6}l4?@W^ PÙ}^n"##w 𬈉qb@P1P1T T T @^-c~8898`T y@@P1P1T T @8 ZxT U*KRP*!b2sd 2U"x r2b1b0RFE}]Mb+z}[H&XZIC j.B\)˗xۤb` dtT;]*-2Yu-#FqCfDRPq8>>- aIea*0O&; gH@UrbDF$*}|4\{WdH$ry\EU!P©p*_eLVP@9R_/:z}a׵##J[aa?EEZz5> ?EE'*ߏ~i|kttjUcD%**á?MQ1dimR<ɗRȶX#oJ7%0,W&+<^wRfIb6BN@i-!"U"wxyU(ku[N,^.>(Yo /DX./}N\(L(,*&ӍrRu@._--eR\dbT.l0G*V(\X&pOZ_'BC1 Sѽ6^o!"⯋cE=N//ktGAA(*^_T%h)^_,>YŶIwH"P1)J$*oT]*1Bh*-ڪrn//<, 3\"T(֚0loo5ߔGhQ<<k*;E٫Gj4V $f?pĺl?{>RxK2<SxrJG,VR`'Y)N˓*{dQ|ksp ,i{*>R1p xTrRa4!xHP*>hx6-sb`l<ϙsb YXXgװN7Jv:0^ߥV ."nTZg@?RU(h~uYG!/w#}BńLWT4୰ޡӽ3aVUJHвeL +-hOD+5R능b$O bvԡa5|gPIbqRC*]/.GjKF\*|L&Zrxeyxx)% b1aNT4"h*_euY|E'xW,tBfP<=W٫eTExݨ(SHZ)T`UxM+f_[{IP܏n >"pbɛY$VGhD)iTfryT8a}\ȗBYwP*R19EutukN&TiT̥rRIhp )6{u7 2QӄSa*_W+S(,4 ǫ.E}";+ ,'<xAzZ#8r6C39W6;*ڨ8(>Q2PXv n֧W1M#8Qbbbbbbbbb** *˖-_>#:&T(2r! .3,зo_"HvUbsb׮]>o>?6vܜ-9_z^?p`UO ֯_pן$yj٪>y^^fOv$L?)ײeܩз~V:/+ߢ&S?T8Jo9Trrlz"CB\MguVC&3'aLc *&x]4*f/= "& qZJ4q7n4PfOvtU3T1e$bׂ%lU[N+عPTL&T T S0bEE22ӷ&c{+,u|t<%&.˛-ܶm~v*}>ԭ['w5/zzZ~ } aꚛ}g͗_QpSKÇ}޾{|7mNMQ__뺺V*nb FdiF_͏HNN& CVV։'FNK%Ћ[6ǴYk"|uv'>GLelKq!օlFL 鸘/cG !Jנ ˬJ#gұw Fz Y[P(E noكbnOV1OS4+/5ٲшAAK$c0, d6mZmY#yH_1#9bB֯ wH&[~{}CB^^ 7wz mGZXŜL'*jaA,yZTa;/rBq j-YcqSǃɜN$?3cj¬J%$.R6Nu^+VX|"0R*Qs͚6W(,v={-ՏTjZqnذ`y~~ZqTCb뙪M?tX?4L^k2sʎЏ?V;̊ @ }Y +y-"cOc( ``̓ixTL& dYzozzZ~>Xhy2"c߾0 S|k _GDN$F PQ囐0/kI ytpbΧeQ "ڦM ֬Y@u^,6mZ@`x^r8ᕔ 1q풑1?&f!66$#Yr:؃h߼ՋBX?*氞ڑި!UP3U{ÉEﺋGSSK1 wOk6X,o>>,ϝ fߩPmm_됁Ѩ_LO<55a*TҨ?q̺2va{OMAI?kzL;z{9Xtb~%7$osLۨXQQT*f3yLBbc۴zpX7x*hӛصneJ3ӷ<|-Td`D4EY\h1J1Vb". تU$ry֛3/4x^` +*jK|'%ҟQŜO'*jafR"ϋFq+-nm]z}~[b%K^ytV̦2ii^#&P|-. }YzAq9={i:61I7q~NJUuuHou؍]fcy 7cUt@Hc~ă#j"qbٽ{qX;R1j[S;M E8T1>4pO1*,\?V86v)UP|JۻwqP|gUL($0?l7LƢb6EŘ>L?+׋EŮ_ чNKΝs~*5kob4^XXF UfWik?'ZNEi^908ê:tw*GG`[ .TWm4%MGlI-;E*eP R1__ǒ)/L@@2@z2ꍏ_fWҮry(mJDoh\jbW1> mKwX"~2JcKXL*fX6KO*TmߣXLzGp)(L@~?ԣ_}EZNL^cj*VWGX(lf? .+Q?AL2&r3|g^QO~v3"g]V1gF~rzV%2&%6';ڒB/EŶlYtVӷ`E֮%sts[?9Br 9~.ӨX<9{jr&5@]0)bQ1 "7lx|'`qr2)2r=KvXE'Y*l 8;s]8ή]RRnذlI F,HK[:ryY3AI2-/,\\Vh?)5IM%8p\?ߑ\Nr2_]V;ZE;=<ss$rr:80ѡOPE"tg*tLL׋gzQK~ُ$1o|(dT?=w{UL @~+rp>DvSYT Ǒ~P_ U]eq_ WΝov,-0kQTMMM__'a+Z.ɧǽ+uմ25綦&3V1d7޹xt4JD-q`#/%qK)A8 1~6 hBY/S ¿/& e"$؝ ,>rX2T:d '=`-~.Ivcc1 o'9޽x:m.LڻlÄTJ.V;j7mZ=ɉXmR C}n?/[ܯObH$o xK폇OUvX!S*lDn.+.^^7vb;w0(ӗHsQ' k$7|J;'XQ9w ,yyX>]@@@@@@@@@@@@@@@@@@P1P1T T >\.σCWbF*fggN3H$jlllnn^jޗLx#tgKii`>msuuj *\ڵksss7o^VFVɸ(QoZ=<7r좾ﺜ[ƂiV^^~e)ov-vr|鐐Jlo˗>ޚ5E[.1O_RS%/_:X7߾f7.>>BTTU_Ѩj KHHHMMuwwd7op8p+We+./-~HPwT* ði8<<9}ϫYT:;9_<=Y}N8p*6+*q:Nрj)JP㸅%%%?JRV+ vS"ʆ!D/RS0m3tcǨnBgS}S-M u:}̐nkԺh lj10>/(hLdV\6ݻ:Vk>u8zt!58bs?who?jaa3{]h:tYP SU1 k3Ur\YQ#A[:vUTl}6,,LTVUUuww(J@VV ܸqB(P__hoo ݽ{LB===*Vkjjzzz땖UfZmGGGTT#Gz{{&S?0???gݺu:jOccU*AބyiiM_BCm244w)TΝy\.v8E<\rP$ `ُ?S%U܄* r\tܘg,xx.O?hppnde~ɉ- TyD˩f?z4V b9_΋{ye&l/MMWZV;-x`LdCCs-tiGs|ݽ;:VZRqCj*y/z!!aIhcSѹ<۶%ϟykk>}05=@ |*yf68p/ gcoe_hq@Iiy֞KV(/u@i*5kkGFۓ۫WWcݻAơ!:,Hڲ_nGFFSVvtzXy$tttt*fF~`j͛7GFFAy9s?wY2@%8x*fq:x!.βaRs-wzgd9T1{kTleo sospg@j3s:A?t_9ny~v;tߟlۗX=G59XDDFdSDTu_#RRȮbV쳘gƤbgbHC.!&MG4&4w> ϢuuuPA:&3ͪ񩯯"^nnniiieee:krb)))]]]*f^lNNT`it7bWڅ~~~k+۫خ]gxz.h8pt=S`%Ǧbv,l˒ݩ؃sRu)˧T1vګbrr??G.3?}* ̮ EEE͠bgffN}\[<@IIy!ԻV?>"(]Z,ب2>kdKp%.RDE9 \W[I OOOq]ŜF/MRMGŨE`עމ| L&T+=#>**jttb% Y$S[ZZD"IXj?ldRjl)DXŞ=;p-_N?:bČ˦YS^yeTŅhr/eSS;Y;:ฌ=@i3?M(L&]׋DG/[LvYuuuMM 𩪘P(H$ZVTJ$pRSS kr~# Bd!}i}i?~D.Eײ|ھm7wLުǟ]Wgjھ؛qGFe+ .B<9O?YRzKHHrTRDRQQ1:::Mv-EqʕJR޻wO*P(iEEE*zҳƍׯWTN݋zsociqq^ *++D,iAn-w0O:. \ܼy3ՠ}{ޅ yLTT,za+V,;yrf>kڵj S~&=/wwGz/Xj}^L{٪=rfiY2g嵵 9*N~܍;^cQծ63XIGFrdfdn]frھ׋P!ۼH$2 )))bIII7 ӓXXzO4O&^123q_'_wm٤ߦzҋYZ(d,쯛z?͓n^E]]^Cl,*bŊk׮777L_hoo?tSZ {dzSSSc|ʦ{~7CCCׯ_ 3oFjzJGx"=ڏ={nݺE-fT*_̾;ۜ;Lr^<__ǷQի ?УLxꅏ3ftcK˂v9wKIYz4v~&=;g9/t{ϋl˞~`Coe91۷\TEFN63Ap=|hZB<={eb1egb. =sfM'SWW?|ʷ4%4=yTY 0W1z2ac1W̚ҡ!B\ tmmm #`$d }iS)U,C17bOuB_Adddjj'******檹?~fOTj7 GH$jlllnn^jմ.TMLR@*T533bDE233gP$XuuVdv8.1$mwi0:tAtSd-a,9ƭ(\ObU,a"iU>Η˗/?'anٝ;w.\@FuV8#Ldee˶oDrrъ^7_xbesTvvv޽{ŋh*33Sh4jBxTaÆRBH? +(:p\ qwBwQhvU LD+@TU|  GDD'Yɓ}}}\\T*#BH$ԖQ1@@׹sNNGvqqEذaZ1?Ysnt{*T*B!v8'&&&$$L&Y˳? {+u𩅊bɓ---TXztrs߿#n" ;L q\W })bdi1EM]1D^$Zb[8ZŴZmGGGTT#Gz{{æ*ƔaaawAPɡ^S{۫SYYY[[T*Qe*OlzjQˍ7F#%S1PjwgMۛ]j_.ɧ.*f&P;ӡ/[aJɨX2=8ogkjjB\Ӣ'EȽ;LP.Id9jb΋)ݞ,&J|˒?--o=ꆇkkkib{R{@555S٨=1ͧYXkkkFF=466f>fbLkjj:@ɤbo(Fb*T䗲7rlbCŨBشiSFF{ҽ)*u_Vs~BN'Byyqd3) SLT|LpE{U`9u!Iz55?ݾ T1b@EX(b+@i}^L̎"GhT2x˒ߦQTVV>|^{0jttt,*988hxh4obL׮];<J%J*d)| ;Lt` + Cg2񶝦T1vګbu,|^ԩSk֬۵k׽{R"Jʮb7nX~J:uT__]tVVS R\\cbbz{{iL*ƔqʕJR޻wO*rʉ'T*j1x,zzzQ=DRQQ1::JSD!%%ô}@ޡ%%%i߄%bڵٹSngC#^̂c]eU_){G{}mGߔawW~9s=a-!  })Bc.'gW.bYDu7޹xtb}2ۋ;PTɣ3sbl&1TAф7eUHW 1Vׅ def*N{UlΗ=Fj\:{ݖkR555A׳޽{曡ׯ[D9^:22BC388xEem.fa3B3 kkk7WDDč7,%^/]D/BbŊk׮777P*T>KqkooYR _Jii,b1W%44Tӵ){P1PzQ8>Ⲇ "##SSS-&كV C=~KII9.!͗c} HP1WWLRiAAq*fQ DGʯ[u˾L4LًV|粰ML-//|#䉻_Pf ¦; effzPQ5Zb(nݺu͚5/R1ϒ.[ B ebS\\\#""f^VjaM^2pD}Qo5"nu:FcW1R)  "ëW(K;]1aIyҍ^eO|Vu 5izQ+qq4/!WoE^//ۈrX_-BScgh:AP&p&qc҈jKD{U'7|dh?b-F*ݓL [qD_ mxCCC,ϊ=u<배זOpCSW1 lU#.f:tٳN$PEL7f[*&󽼼*4g[|yZŜ*K9vכE QhT4H2MYJM&K5ѤbN"dm?ޜ܍"j~:З|Z8(_eq^]ZZN8e|斉=@D^ĎKXZT_+lT Pn$g5~M^bm޼9..LaỤ Y4JU4ʯ`\BH+CYE3M99wԡ:7tJ'Fr wYKz5a"i[oU|[n< rǛ󉱪݁|,soxpY;sYă#T~4.W6U _G&䕞&$[MwD7 ?q ,b=J*}2*JO8TIbd݉>Ulo#tP1:g_ VTL4~&|\9:cseTAlj KG96*VTT _=b۶m+,,,{Aڤ&Lmt-gz*%suN"g]V1gF~rӏ*Jd!@i'H ʟDNhK Xɂs0R9B(Y{GxnHd!!!}*P(H$ZVTJ$)^U91!.hsYuwI*Gy޶FJvǒctu|p9}W,aU{<&Ŧ[A7}ی!+=ģQ"j1 })_LQPa_© ٳT **yyu>o O]3j XU TP1P1@@>*#M7 {w`B9ֿ,bf\J+Jxaaa:h4/D~ bֈWjT_9ŔͲᅭu=G$t ,)?%O:lJ?}x TlZDb# +XJJ^OJJPJoooP1k8J4\.xD'[GKzQo٦bsTiԁ =W좳3ecX dzPӧO|ʗD:?}wOҍ՜tT _|]knjZU_@O$h?=/ptcWtqB=b*W*(Ջ^?ނzlnNM7BxyH* U 9ܒ'ؾ[x(7Úpr/=hAMVS XX}}}ee7|뛛CCC?{E]|j CHoWd" ˖3^eO8JmD yg*xc;PI7;!WYru ~]>iEsݦ׉قӕsWـF{8=)-}na=,2 U6 e3Ʀ70Ƥbqqq}}}V1R^lqIȃId)T1rK*OXY>^GSAI>5Ytu*S*FLd2J2񞞞AʎӧOt:.gb:8N- .uT xc y Ы[^{Q/_;U{ 8WzW,H⺇$z< {PgF"nwnoxulbkTH&!d3|?|ApI>x ~ܝG#P̲*ƵɀL|A/EŦ9$`|V喗 g|XU *FwǞAwzD(M|{3={:&u|{OTΓn:}ϑtSCeS"x[t'T99|{K17:a~!sݢ?_^" Ÿy–aaV [E?\ELLKګb6^—=?|V*f>$bzY*{[O5K T PT1!;$*bP%@@@@@U533S* bǎѠb>FVT謬,y~P1@fR 8[KaaTl&T{TmULTl޼yǎIII2 T Pb...111;A{[W*􏹹?*U,''v/EA}BD"qvvjJR"x&v"a&8SirblO\ʡ!ˊE~YѵgzErCSW1U7"Kֻ|!OYoeU_)zp!rW-GsWS1Cw]{hUo!nprd|ۍBؾxbYaQ1c7tB_LIƾ WɰD g„m˒ HZZN="8߇D}ek4*Vz3It'q Qk"cyE߸qh4R[l bɺk[(bdzT+oW$q y !EfzfJ4M3*U_~!TvXuuuMMEL& T  Y*JDB߳qh4-T91!.hsYuwI*Gy޶F>+FݱW#Xf|5 U>]0"âb=Hu:~*oRe䣔U#1>n}c&D6,4/ҍ#N.<1,: <*lAw^U6@rN}JD!%%"b&,ro\.cP1+g4TY@b{|sn\WŐxQŁ\l/yD(Lb0&Lfoe3n"¿/& e"$؝ \Rb GzZ=?|QxH7._kzt\̢R2,*"8M=2;z]pkooMs:Ulցl-'?zػthhhϞ=CCCu:][[bb-3o/OC$,,:㭖T T P1P1P=@@r%%% 狀C}DUV]\\JKK Cdd$?؇Q1WWL1 [fMvvv^^^RRL&fi_[/Xr3Ǯ?ϥ^OHH0O1իg,dK–_|KWgW<Ϸ3r233zP(*PbFV[XTTTffL& k׮h4;j[-l%wIǟ\^"TԩS*3lU'H2{{{Uexx8""?ypKKr}Ν .Гz}}1ZA9YMM͡CIDEE۷z{{Ѳb*hg:/[nYXllАeOo$(KC={@޷QXXhhhFFX,0,***55;.="8߇D}ekĎKXZT_+lT PnD $zMdb6,,LTVUUuww(|}}w=00 YҵZmGGGTT#Gz{{0_VV@VV ܸq#A@NNZ$XVV`=tݻwUXT,--M쥷W)mmm*PEYzV-,,LOO+啞NI$V.O g#S"z=B0UWW[aJommȠ^sܱPJz{{l6ccc#|2^___VVFH$2&c:/vkooP*sXT,<<<--[jUff&A䙛Db6"dS!~EgT1:}ơ!:PRYvpkkkuuuzz:=)sppZ_NWT*YT|Otvc:/P1TlZ*k prssoi9="h4/ \b“>8\,XWWWII Ltqq1W772NA?Wh33'' .kbT1J{U弘T,%%CXQQQss3|U}D*FOFQ4??_T~*9n:_ ܬL^OqThWRGVe2h dIomm-**qT)@YSSCj(HX΋ZŢFGGm ΤP111]Ūkjj T}P(ZVTJ$^nݺ۷+ HnݺjsU1mXh4ݱ_G\yW%+cXtyT:' j*Pe 9'L>QCsF$IEE(, +WT*Z޽{R%XRUʕ+'NSTugZ";;~U*m۶?::׷1ťKjkkjJruu|^ߺuy 1+.UW-TӴHd0RRR T}XRRMé V^cǎDJ|WDqw}ͦt- "N MB纩G&yG/Mbuuuz\JVXqڵ栠 `0ܻw->>ϔN388xEjlJq=ƴ+˭uVBB0a޽{{zz~ᇆooo zȈh5^Wtkn+)frpnll,++k:ۧ*Q} sfҡ={L3&&fhhhIt6'P +,,2ہla6'bbbA?bb***************bY.~ҁ:o_ɕ'ewJ|T >>kJ8ΩXt;ҿjaau}݂qvho|''ŋ ~[n\P13Ǐ:65qٶmKkk>} O/ljj2ڱnsQRFF. \VY8p!>a;rds)p}@z{ݻz-.!4klB*E955 -qww|ǑK;pp y/ W 3*66FʟΉ] HgE%9vu9}" `K6,9Ν[X, )(X\=x0/"zfKŌF*e[<[]'o )U AO͒':ܻ7z/vq⺺EV ݼ9f̃7tv&*$oFŅhr/e*f]>MJ~#q~ >57/8q'sAA<yW>*b wН.}U,++d ׯ3syC998獷lj~ix}%zrݫŠbD5AooMtUUU*u7bN\deЍ{]W[֐n]W{pU_NƝOWW||su߿?TKP5k֠( *|*&O<9000<<Is}uww߹s… uM +kjjFGGR}SG4},!05ֺu*ҀM-''ia)f~&C{8s:uiz WBCGJgzxxO" O4OĜXUaaO轑Ô~zU1zC8}l~6qgJϾ}B;_>~~/_:XxG>x0 Ɲd;8/.G7oݺeC]݂E2۷e)UU._^ۻ&: I$\BH Ȃ\J)j`T*R) l/X4*FPnKƶϳ}|Ͼg/8:!3$5?pfd|rOfKJz||֟8ƽ{MA}.^5oqG(aԠvi!H@ >}|S}mMo4" 1_|2d7Fxݼ<:4շ?}L-A“=ޡtFԪ^?n4D5ĈbH!:[,+<;w.).=xXcf$< D1xXEEEGGNh4H$EtwwjSN D"(P( h!998&1 MMM4MHҤ-sEs\KmzmmzEQԥKSJKv}WZ(/o`f1;wtm Q9Vz8g([͝4cKJo 8mˈb.U^۰1->1+wBصk?Mx8cޘ^ֆ o;ڿ_֬ycbb6.nɓow/P}MQћoB9}urr֭oo_M?YCJo|ݚ޾xu&|PP*;5;w< jh6u]- )wR,wj:|1G=.qש  ==YyQ¨T䭟|ʖdwk$YjL^v[}{F5)MJ%TUW[<2go=6n$q˖-7nܠij# Frټ{nlvv611- 2p9::ZXXHsPFDD(˱+Wh +~>~'H6m++0 -+ˇ3N/|4?bsEs7\ wD`&$eEfs?v\( tbKS4b{w"j>~h؏z;.w9 84ʁo"Ѻ ׯq|KJ~mG+|Fybls P>ܮF3yTw RVķIee4f‹u ʕ kS1?~aa > &Zl6,'2]D}+Kz}ccNC'OMMfѸk.FOOĄҞf3D(q }|L+)adtRıQ̋Řq ?:;"no%bvs9mˈb.+Q̋L'K]z)OWxq>4=Eo|9;ގ~ᥡW.\xMd;Q,#cc󁉉blsD1f\6G5~ӚKYYo9?\==N j ѽ+C+wpJ8ϵŜ< h*J\bb1z?27c rB(G#'w~~~eeb&===)$RıQ+f)-/">Q+oܮ #OWxqEO?}3*jDSWd}~y33/OO]dc~tbl{hus,wbܯc.PfR_W>KCJ+_S]#|+~P? l+'Jl-yZ1XZ%2k#jw鉉cǎ#D"'e2MqqqdlQ}uHy,qhc﷼B~ 7|Z&$RBSl?@ͽ|wyQ}۟+8a ?] PVTĢ:+4t>tosg- 4@8_N>04a94;{]|dXDCQTOz-^e 0h~ʯ@1@rFeۓhQ2g(C==ْ쇯o4=22'x|D1x>XSSdJNNVT%%%AA/Ù񕕕b+WĨПaQrr  -^`KI뜯3o9wvý>=W4WT ܊2sEs-|巅ߎ8iD`}jkneڗ޳ؙi.{ToZ^tpijsSl;^9Z|4yum[7=sחW{{_MO_co/d8ablo ɴק(oyKKUaU*J)T}5ii1OC>E""F预Q}}z<ݮ^TNIx+J[[5ݙLPu803mFB8vM6[y,*%_wnݺUPPQ (&l6t{{{NN) Vubbˤ̱|dgbәGw ?M>?{{w̨N潽~#;G9ֹ}x>7ݴؓNriVKD5ǓrO#sD#Gm7u2;y]}n w7n;r&-mԚ^fF}7?by.̎xƍD/w=lQ~zآ@o}x3 rV噙o(xUyu f9JZխw|)}E/J8O6@w׊oo_icNA[T-w4D50eQg? aiV6oݰ##NbF1x|e5i5N D 8,Ųb (+N-U/d}C8N''F:bb(@ X?> VW%<^Wp jPO3ݹjjZ4=B99k_C;ӌbmۆ(Ѿ}S@9XKkOUƍ.^D<(e;w0LX^ƄixQoyEʩSo64`\(VZfgkr9 ?p|&8>xyg?Ǘ` ~5fkvX`D1u.Qr`F tGY"Z#~~FIXC@1 F7BQ,](4ѷ ? 1K/%`ێ+.}(("Npg#bxH= ,X` 2- v{DH? c`񸸿uQQaas),#u#%Bq"3*BC 3i2>Gq*B }2`OgJMsօh,TܟߔX?0(G9Q`F1@@D1D1@@D1D1@{*D&zԔl^߰NX,4M'$$0Rɓ6… n~֭[|UO^^MXv ȑ#}}}6IVsnLP(O{zzl6ŋ~(vHZ]VV6>>ŭ{NQ͛)))qqq.\|S|>5Z֌غ;wBѣ}}}~~~Lɑ#Gz{{322jjj ERޝ;wޗ[MV0L8wݻwfs||Yӑi`DTҋ,% IݻwZ-Dr̙ѩ7n z\!33!//)}fsaa!y,Jic[ݴZnt۷fedd,xb(ɓ:NRUWWx<5n߾VAUUUOO)Ŝt:R)۟ BX,SURRݝjO:500 (tСCs?s;;t1r9b2IyDDwoQ… Eټ{nX(&&&RUWWg4ݯ{},999===668499 Ñd@43&(gϞQw311a{ieF]&H-{}G^1vtbIKK1###L.LQr'<^rR*Fd21彽d/88xXIIȒXaaaf{XXX~~~eeb&V+Ř݌bmrrr˖-1Ϸlo%Irr,xQ PMOOo޼@5 YGV{= Zm,"l>x-2ldNýE1iIIIvY(ńBa{{{ee/H(\V+Rĉ333ˋb2)霜H2pvƍvN|wchmm=}tttZ&8Ù񕕕d~SSdJNNVT%%%AAAp\.g,Zm]]bqruLlvQ2@jK12G1\>99Sq[n E17pg`Nwuu28BjNLL\|ڑ 6mzz=''sekXSSJrmCC0s_VvDeee#L&%=:^X(bb((bb((bb((bbb((bb((bP(,//ojj3 믵Z-Şbd2u_PP޽{CCCx~)Gl#(VWWG?n֭o]fs% EQeee.{FQd"?rwׯ_D, M ^yyy4M8p`/4M?aEQ7o陝uYsz~ ;v<={2 4׿tG~%J:Ĭ'X,<;֪M&Syyydddbbbssskk+_J655~JxjzJË,KuuFQՁKJR600D1Y,}eVic䴵練8ñ#UUU-.7L̛P5 deeMNN: #Gݽ{tRPP`29]KKK{{{ƚ%̙3SSS7nXrx%''G.D' luby333o6:IIIvdwO<e||d2QT*EH&VE=TZSS322bٚI9K;ER8<>N&1׆\Y\vd7hkk[V Nכʁ2i;pӴ}@~SSdJNNVT%%%AAA;/"##E4'''221p.{um,..$Өr93jDWvuum޼YVV·쌈DKF odffNMM}tҹs(qm߾}lll֭Ǐ'jڻV+y"JO8133Ca{~+W0;wڴiFihh`s/m6y@<33ZW_>}N-^9} 0%{V˗[eD1ÇYT*ryCCfnooY2BlrqeNm1 .nʫW={vrr̴*rv?}ؘbZ2jx\O@@ Ӌ111,vΝ f ^EnJ~ab7nuPKKK||n'=D\\\[[H&)C=4.2", "rich>=10", ] urls = { Changelog = "https://github.com/adamchainz/django-rich/blob/main/CHANGELOG.rst", Funding = "https://adamj.eu/books/", Repository = "https://github.com/adamchainz/django-rich" } [dependency-groups] test = [ "coverage[toml]", "pytest", "pytest-django", "pytest-randomly", "rich", ] django42 = [ "django>=4.2a1,<5; python_version>='3.8'" ] django50 = [ "django>=5.0a1,<5.1; python_version>='3.10'" ] django51 = [ "django>=5.1a1,<5.2; python_version>='3.10'" ] django52 = [ "django>=5.2a1,<6; python_version>='3.10'" ] [tool.isort] add_imports = [ "from __future__ import annotations", ] force_single_line = true profile = "black" [tool.pyproject-fmt] max_supported_python = "3.13" [tool.pytest.ini_options] addopts = """\ --strict-config --strict-markers --ds=tests.settings """ django_find_project = false xfail_strict = true [tool.coverage.run] branch = true parallel = true source = [ "django_rich", "tests", ] [tool.coverage.paths] source = [ "src", ".tox/**/site-packages", ] [tool.coverage.report] show_missing = true [tool.mypy] enable_error_code = [ "ignore-without-code", "redundant-expr", "truthy-bool", ] mypy_path = "src/" namespace_packages = false strict = true warn_unreachable = true [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [tool.rstcheck] report_level = "ERROR" [tool.uv] conflicts = [ [ { group = "django42" }, { group = "django50" }, { group = "django51" }, { group = "django52" }, ], ] django-rich-2.0.0/src/000077500000000000000000000000001500736107700144735ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/000077500000000000000000000000001500736107700167425ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/__init__.py000066400000000000000000000000001500736107700210410ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/management/000077500000000000000000000000001500736107700210565ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/management/__init__.py000066400000000000000000000032051500736107700231670ustar00rootroot00000000000000from __future__ import annotations import sys from typing import Any from typing import TextIO from django.core.management import BaseCommand from django.core.management import CommandError from rich.console import Console class RichCommand(BaseCommand): def __init__( self, stdout: TextIO | None = None, stderr: TextIO | None = None, no_color: bool = False, force_color: bool = False, ): super().__init__(stdout, stderr, no_color, force_color) self._setup_console(stdout, no_color, force_color) def execute(self, *args: Any, **options: Any) -> str | None: force_color: bool = options["force_color"] no_color: bool = options["no_color"] stdout: TextIO | None = options.get("stdout") # Duplicate check from Django, since we’re running first. if force_color and no_color: raise CommandError( "The --no-color and --force-color options can't be used together." ) self._setup_console(stdout, no_color, force_color) return super().execute(*args, **options) def _setup_console( self, stdout: TextIO | None, no_color: bool | None, force_color: bool | None, ) -> None: force_terminal: bool | None = None if no_color: force_terminal = False elif force_color: force_terminal = True self.console = self.make_rich_console( file=(stdout or sys.stdout), force_terminal=force_terminal, ) def make_rich_console(self, **kwargs: Any) -> Console: return Console(**kwargs) django-rich-2.0.0/src/django_rich/management/commands/000077500000000000000000000000001500736107700226575ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/management/commands/__init__.py000066400000000000000000000000001500736107700247560ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/management/commands/shell.py000066400000000000000000000004531500736107700243420ustar00rootroot00000000000000from __future__ import annotations from typing import Any from django.core.management.commands.shell import Command as BaseCommand from rich import pretty class Command(BaseCommand): def handle(self, **options: Any) -> None: pretty.install() return super().handle(**options) django-rich-2.0.0/src/django_rich/py.typed000066400000000000000000000000001500736107700204270ustar00rootroot00000000000000django-rich-2.0.0/src/django_rich/test.py000066400000000000000000000227331500736107700203020ustar00rootroot00000000000000from __future__ import annotations import io import sys import unittest from collections.abc import Iterable from types import TracebackType from typing import Any from typing import Union from typing import cast from unittest.case import TestCase from unittest.case import _SubTest # type: ignore [attr-defined] from unittest.result import STDERR_LINE from unittest.result import STDOUT_LINE from unittest.result import TestResult from unittest.result import failfast from unittest.runner import _WritelnDecorator from django.test import testcases from django.test.runner import DebugSQLTextTestResult from django.test.runner import DiscoverRunner from django.test.runner import PDBDebugResult from rich.color import Color from rich.console import Console from rich.rule import Rule from rich.style import Style from rich.table import Table from rich.traceback import Traceback _SysExcInfoType = Union[ tuple[type[BaseException], BaseException, TracebackType], tuple[None, None, None], ] DJANGO_GREEN = Style(color=Color.from_rgb(32, 170, 118)) DJANGO_GREEN_RULE = Rule(style=DJANGO_GREEN) RED = Style(color="red") YELLOW = Style(color="yellow") class RichTextTestResult(unittest.TextTestResult): # Declaring attribute as _newline was added in Python 3.11. _newline: bool def __init__( self, stream: _WritelnDecorator, *args: Any, **kwargs: Any, ) -> None: super().__init__(stream, *args, **kwargs) self.console = Console( # Get underlying stream from _WritelnDecorator, normally sys.stderr: file=self.stream.stream, ) with self.console.capture() as cap: self.console.print(Rule(characters="═", style=DJANGO_GREEN)) self.separator1 = cap.get().rstrip("\n") with self.console.capture() as cap: self.console.print(Rule(characters="━", style=DJANGO_GREEN)) self.separator2 = cap.get().rstrip("\n") if sys.version_info < (3, 11): self._newline = True def startTest(self, test: TestCase) -> None: super().startTest(test) if sys.version_info < (3, 11): self._newline = False def addSuccess(self, test: TestCase) -> None: if self.showAll: self._write_status(test, "ok") elif self.dots: self.console.print(".", style=DJANGO_GREEN, end="") @failfast def addError(self, test: TestCase, err: _SysExcInfoType) -> None: self.errors.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True if self.showAll: self._write_status(test, "ERROR") elif self.dots: self.console.print("E", style=RED, end="") @failfast def addFailure(self, test: TestCase, err: _SysExcInfoType) -> None: self.failures.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True if self.showAll: self._write_status(test, "FAIL") elif self.dots: self.console.print("F", style=RED, end="") def addSkip(self, test: TestCase, reason: str) -> None: self.skipped.append((test, reason)) if self.showAll: self._write_status(test, f"skipped {reason!r}") elif self.dots: self.console.print("s", style=YELLOW, end="") def addExpectedFailure(self, test: TestCase, err: _SysExcInfoType) -> None: self.expectedFailures.append((test, self._exc_info_to_string(err, test))) if self.showAll: self.console.print("expected failure", style=YELLOW) elif self.dots: self.console.print("x", style=YELLOW, end="") @failfast def addUnexpectedSuccess(self, test: TestCase) -> None: self.unexpectedSuccesses.append(test) if self.showAll: self.console.print("unexpected success", style=RED) elif self.dots: self.console.print("u", style=RED, end="") def printErrorList( self, flavour: str, errors: Iterable[tuple[TestCase, str]], ) -> None: for test, err in errors: title = f"{flavour}: {self.getDescription(test)}" self.console.print(DJANGO_GREEN_RULE, title, DJANGO_GREEN_RULE) self.stream.write("%s\n" % err) def _write_status(self, test: TestCase, status: str) -> None: is_subtest = isinstance(test, _SubTest) if is_subtest or self._newline: if not self._newline: self.console.print("\n", end="") if is_subtest: self.console.print(" ", end="") self.console.print(self.getDescription(test), end="") self.console.print(" ... ", end="") if status == "FAIL": self.console.print("FAIL", style=RED) elif status == "ERROR": self.console.print("ERROR", style=RED) elif status.startswith("skipped"): self.console.print(status, style=YELLOW) else: self.console.print(status, style=DJANGO_GREEN) self._newline = True def addSubTest( self, test: TestCase, subtest: TestCase, err: _SysExcInfoType | None ) -> None: if err is not None: if self.showAll: if issubclass(err[0], subtest.failureException): # type: ignore [arg-type] self._write_status(subtest, "FAIL") else: self._write_status(subtest, "ERROR") elif self.dots: if issubclass(err[0], subtest.failureException): # type: ignore [arg-type] self.console.print("F", style=RED, end="") else: self.console.print("E", style=RED, end="") TestResult.addSubTest(self, test, subtest, err) def _exc_info_to_string(self, err: _SysExcInfoType, test: TestCase) -> str: """Converts a sys.exc_info()-style tuple of values into a string.""" exctype, value, tb = err if hasattr(self, "_clean_tracebacks"): # Post-bpo-24959 - merged to Python 3.11, backported to 3.9 and 3.10 tb = self._clean_tracebacks(exctype, value, tb, test) else: # pragma: no cover # needed on old 3.9 and 3.10 patch versions, but not testing those # Skip test runner traceback levels while tb and self._is_relevant_tb_level(tb): # type: ignore [attr-defined] tb = tb.tb_next msgLines = [] if exctype is not None: # pragma: no branch # can't work when this isn't true assert value is not None extract = Traceback.extract(exctype, value, tb, show_locals=True) tb_e = Traceback(extract, suppress=[unittest, testcases]) with self.console.capture() as capture: self.console.print(tb_e) msgLines.append(capture.get()) if self.buffer: assert isinstance(sys.stdout, io.StringIO) output = sys.stdout.getvalue() assert isinstance(sys.stderr, io.StringIO) error = sys.stderr.getvalue() if output: if not output.endswith("\n"): output += "\n" msgLines.append(STDOUT_LINE % output) if error: if not error.endswith("\n"): error += "\n" msgLines.append(STDERR_LINE % error) return "".join(msgLines) class RichDebugSQLTextTestResult(DebugSQLTextTestResult, RichTextTestResult): # DebugSQLTextTestResult adds sql_debug to errors in type-incompatible way def printErrorList( # type: ignore [override] self, flavour: str, errors: Iterable[tuple[TestCase, str, str]], ) -> None: for test, err, sql_debug in errors: title = f"{flavour}: {self.getDescription(test)}" self.console.print(DJANGO_GREEN_RULE, title, DJANGO_GREEN_RULE) self.stream.write("%s\n" % err) self.console.print(DJANGO_GREEN_RULE) self.console.print(sql_debug) class RichPDBDebugResult(PDBDebugResult, RichTextTestResult): pass class RichTestRunner(unittest.TextTestRunner): resultclass = RichTextTestResult def _printDurations(self, result: RichTextTestResult) -> None: if not result.collectedDurations: return ls = sorted(result.collectedDurations, key=lambda x: x[1], reverse=True) if cast(int, self.durations) > 0: # typeshed has a bad hint (?!) ls = ls[: cast(int, self.durations)] table = Table(title="Slowest test durations", title_style=YELLOW) table.add_column("Duration", justify="right", no_wrap=True) table.add_column("Test") hidden = False for test, elapsed in ls: if self.verbosity < 2 and elapsed < 0.001: hidden = True continue table.add_row("%.3fs" % elapsed, test) if hidden: table.caption = ( "Durations < 0.001s were hidden. Use -v to show these durations." ) if table.rows: result.console.print(table) else: result.console.print(table.title, style=table.title_style) result.console.print(table.caption, style="table.caption", highlight=False) class RichRunner(DiscoverRunner): test_runner = RichTestRunner def get_resultclass(self) -> type[unittest.TextTestResult] | None: if self.debug_sql: return RichDebugSQLTextTestResult elif self.pdb: return RichPDBDebugResult return None django-rich-2.0.0/tests/000077500000000000000000000000001500736107700150465ustar00rootroot00000000000000django-rich-2.0.0/tests/__init__.py000066400000000000000000000000001500736107700171450ustar00rootroot00000000000000django-rich-2.0.0/tests/settings.py000066400000000000000000000007161500736107700172640ustar00rootroot00000000000000from __future__ import annotations from typing import Any SECRET_KEY = "NOTASECRET" ALLOWED_HOSTS: list[str] = [] DATABASES: dict[str, dict[str, Any]] = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": "testdb", } } DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" INSTALLED_APPS = [ "django_rich", "tests.testapp", ] MIDDLEWARE: list[str] = [] TEST_RUNNER = "django_rich.test.RichRunner" USE_TZ = True django-rich-2.0.0/tests/test_management.py000066400000000000000000000057051500736107700206020ustar00rootroot00000000000000from __future__ import annotations import os from inspect import Parameter from inspect import Signature from inspect import signature from io import StringIO from typing import Any from unittest import mock import pytest from django.core.management import BaseCommand from django.core.management import CommandError from django.core.management import call_command as base_call_command from django.test import SimpleTestCase from rich.console import Console from django_rich.management import RichCommand from tests.testapp.management.commands.example import Command as ExampleCommand def strip_annotations(original: Signature) -> Signature: return Signature( parameters=[ param.replace(annotation=Parameter.empty) for param in original.parameters.values() ] ) class FakeTtyStringIO(StringIO): def isatty(self) -> bool: return True def call_command(command: BaseCommand | str, *args: str, **kwargs: Any) -> None: # Ensure rich uses colouring and consistent width with mock.patch.dict(os.environ, TERM="", COLUMNS="80"): base_call_command(command, *args, **kwargs) class RichCommandTests(SimpleTestCase): def test_init_signature(self): rc_signature = strip_annotations(signature(RichCommand.__init__)) assert rc_signature == signature(BaseCommand.__init__) def test_execute_signature(self): rc_signature = strip_annotations(signature(RichCommand.execute)) assert rc_signature == signature(BaseCommand.execute) def test_combined_color_flags_error(self): with pytest.raises(CommandError) as excinfo: call_command("example", "--no-color", "--force-color") assert ( str(excinfo.value) == "The --no-color and --force-color options can't be used together." ) def test_output_non_tty(self): stdout = StringIO() call_command("example", stdout=stdout) assert stdout.getvalue() == "Alert!\n" def test_output_tty(self): stdout = FakeTtyStringIO() call_command("example", stdout=stdout) assert stdout.getvalue() == "\x1b[1;31mAlert!\x1b[0m\n" def test_output_tty_no_color(self): stdout = FakeTtyStringIO() call_command("example", "--no-color", stdout=stdout) assert stdout.getvalue() == "Alert!\n" def test_output_force_color(self): stdout = StringIO() call_command("example", "--force-color", stdout=stdout) assert stdout.getvalue() == "\x1b[1;31mAlert!\x1b[0m\n" def test_output_make_rich_console(self): class TestCommand(ExampleCommand): def make_rich_console(self, **kwargs: Any) -> Console: return super().make_rich_console( **kwargs, markup=False, highlight=False ) stdout = FakeTtyStringIO() call_command(TestCommand(), stdout=stdout) assert stdout.getvalue() == "[bold red]Alert![/bold red]\n" django-rich-2.0.0/tests/test_shell.py000066400000000000000000000024741500736107700175750ustar00rootroot00000000000000from __future__ import annotations import sys from code import InteractiveConsole from unittest import mock import django import pytest from django.core.management import call_command from django.test import SimpleTestCase from django.test.utils import captured_stdin from django.test.utils import captured_stdout class ShellCommandTestCase(SimpleTestCase): @pytest.mark.skipif( sys.platform == "win32", reason="Windows select() doesn't support file descriptors.", ) @mock.patch("django.core.management.commands.shell.select") def test_pretty(self, select): console = InteractiveConsole() with captured_stdin() as _, captured_stdout() as stdout: call_command("shell", "-i", "python") console.push("from rich.panel import Panel\n") console.push('Panel.fit("hi!")\n') lines = stdout.getvalue().splitlines() if django.VERSION >= (5, 2): assert lines == [ "0 objects imported automatically.", "", "╭─────╮", "│ hi! │", "╰─────╯", ] else: assert lines == [ "╭─────╮", "│ hi! │", "╰─────╯", ] django-rich-2.0.0/tests/test_test.py000066400000000000000000001127311500736107700174430ustar00rootroot00000000000000from __future__ import annotations import inspect import os import re import subprocess import sys import time import unittest.case from pathlib import Path from textwrap import dedent import django import pytest from django.db import connection from django.test import SimpleTestCase from django.test import TestCase from django.test.runner import DiscoverRunner @pytest.mark.skip(reason="Run below via Django unittest subprocess.") class ExampleTests(TestCase): def test_pass(self): self.assertEqual(1, 1) def test_error(self): raise ValueError("Woops") def test_failure(self): self.assertEqual(1, 2) def test_failure_django_assertion(self): self.assertURLEqual("/url/", "/test/") def test_failure_sql_query(self): with connection.cursor() as cursor: cursor.execute( "SELECT 1234, %s", (5678,), ) self.assertTrue(False) def test_skip(self): self.skipTest("some reason") @unittest.expectedFailure def test_expected_failure(self): self.assertEqual(1, 2) @unittest.expectedFailure def test_unexpected_success(self): self.assertEqual(1, 1) def test_pass_output(self): print("This is some example output") print("This is some example output", file=sys.stderr) def test_failure_stdout(self): print("This is some example output") self.assertTrue(False) def test_failure_stdout_no_newline(self): print("This is some example output", end="") self.assertTrue(False) def test_failure_stderr(self): print("This is some example output", file=sys.stderr) self.assertTrue(False) def test_failure_stderr_no_newline(self): print("This is some example output", end="", file=sys.stderr) self.assertTrue(False) def test_slow(self): # Ensure over the 0.001s threshold time.sleep(0.002) def test_subtest(self): for i in range(0, 2): with self.subTest(i=i): self.assertEqual(i, i) def test_failure_subtest(self): for i in range(0, 2): with self.subTest(i=i): # 1 is not even. self.assertEqual(i % 2, 0) def test_skip_subtest(self): with self.subTest("success", a=1): pass with self.subTest("skip", b=2): self.skipTest("skip") def test_mixed_subtest(self): with self.subTest("success", a=1): pass with self.subTest("skip", b=2): self.skipTest("skip") with self.subTest("error", c=3): # type: ignore [unreachable] self.fail("fail") with self.subTest("error", d=4): raise Exception("error") @pytest.mark.skip(reason="Run below via Django unittest subprocess.") class TearDownFailTests(TestCase): def tearDown(self): raise AssertionError("fail") def test_tearDownError_success(self): self.assertEqual(1, 1) def test_tearDownError_fail(self): self.assertEqual(1, 2) def test_tearDownError_error(self): raise ValueError("Woops") @pytest.mark.skip(reason="Run below via Django unittest subprocess.") class TearDownErrorTests(TestCase): def tearDown(self): raise Exception("error") def test_tearDownError_skip(self): self.skipTest("skip") PYPROJECT_PATH = Path(__file__).resolve().parent.parent / "pyproject.toml" class TestRunnerTests(SimpleTestCase): def test_test_runner_base_class(self): # RichTestRunner currently inherits from unittest.TextTestRunner, which # is what Django’s DiscoverRunner uses by default. This test ensures # that has not changed in a future Django version. assert DiscoverRunner.test_runner is unittest.TextTestRunner def run_test( self, *args: str, input: str | None = None, width: int = 80, ) -> subprocess.CompletedProcess[str]: return subprocess.run( [ "python", "-m", "django", "test", *args, ], input=input, capture_output=True, text=True, env={ **os.environ, "DJANGO_SETTINGS_MODULE": "tests.settings", "COVERAGE_PROCESS_START": str(PYPROJECT_PATH), # Ensure rich uses colouring and consistent width "TERM": "", "COLUMNS": str(width), }, ) def test_does_not_exist(self): result = self.run_test("does_not_exist") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[:6] == [ "E", "─" * 80, "ERROR: does_not_exist (unittest.loader._FailedTest.does_not_exist)", "─" * 80, "ImportError: Failed to import test module: does_not_exist", "Traceback (most recent call last):", ] else: assert lines[:6] == [ "E", "─" * 80, "ERROR: does_not_exist (unittest.loader._FailedTest)", "─" * 80, "ImportError: Failed to import test module: does_not_exist", "Traceback (most recent call last):", ] def test_pass_quiet(self): result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_pass") assert result.returncode == 0 assert result.stderr.splitlines()[:1] == [ "━" * 80, ] def test_pass_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_pass") assert result.returncode == 0 assert result.stderr.splitlines()[1:3] == [ ".", "━" * 80, ] def test_pass_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_pass") assert result.returncode == 0 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_pass (tests.test_test.ExampleTests.test_pass) ... ok", "", "━" * 80, ] else: assert lines[1:4] == [ "test_pass (tests.test_test.ExampleTests) ... ok", "", "━" * 80, ] def test_error_quiet(self): result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_error") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[:2] == [ "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests.test_error)", ] else: assert lines[:2] == [ "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_error_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_error") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "E", "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests.test_error)", ] else: assert lines[1:4] == [ "E", "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_error_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_error") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:5] == [ "test_error (tests.test_test.ExampleTests.test_error) ... ERROR", "", "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests.test_error)", ] else: assert lines[1:5] == [ "test_error (tests.test_test.ExampleTests) ... ERROR", "", "─" * 80, "ERROR: test_error (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_failure_quiet(self): result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_failure") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[:2] == [ "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests.test_failure)", ] else: assert lines[:2] == [ "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_failure_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_failure") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "F", "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests.test_failure)", ] else: assert lines[1:4] == [ "F", "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_failure_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_failure") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:5] == [ "test_failure (tests.test_test.ExampleTests.test_failure) ... FAIL", "", "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests.test_failure)", ] else: assert lines[1:5] == [ "test_failure (tests.test_test.ExampleTests) ... FAIL", "", "─" * 80, "FAIL: test_failure (tests.test_test.ExampleTests)", ] assert "─ locals ─" in result.stderr def test_failure_stack_frames(self): result = self.run_test( "-v", "2", f"{__name__}.ExampleTests.test_failure", width=1000 ) assert result.returncode == 1 assert "unittest/case.py" not in result.stderr assert "in _baseAssertEqual" not in result.stderr def test_skip_quiet(self): result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_skip") assert result.returncode == 0 assert result.stderr.splitlines()[:1] == [ "━" * 80, ] def test_skip_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_skip") assert result.returncode == 0 assert result.stderr.splitlines()[1:3] == [ "s", "━" * 80, ] def test_skip_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_skip") assert result.returncode == 0 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ ( "test_skip (tests.test_test.ExampleTests.test_skip) ... " + "skipped 'some reason'" ), "", "━" * 80, ] else: assert lines[1:4] == [ "test_skip (tests.test_test.ExampleTests) ... skipped 'some reason'", "", "━" * 80, ] def test_expected_failure_quiet(self): result = self.run_test( "-v", "0", f"{__name__}.ExampleTests.test_expected_failure" ) assert result.returncode == 0 assert result.stderr.splitlines()[:1] == [ "━" * 80, ] def test_expected_failure_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_expected_failure") assert result.returncode == 0 assert result.stderr.splitlines()[1:3] == [ "x", "━" * 80, ] def test_expected_failure_verbose(self): result = self.run_test( "-v", "2", f"{__name__}.ExampleTests.test_expected_failure" ) assert result.returncode == 0 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ ( "test_expected_failure (tests.test_test.ExampleTests." + "test_expected_failure) ... expected failure" ), "", "━" * 80, ] else: assert lines[1:4] == [ ( "test_expected_failure (tests.test_test.ExampleTests) ... " + "expected failure" ), "", "━" * 80, ] def test_unexpected_success_quiet(self): result = self.run_test( "-v", "0", f"{__name__}.ExampleTests.test_unexpected_success" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[:1] == ["═" * 80] else: assert lines[:1] == ["━" * 80] def test_unexpected_success_normal(self): result = self.run_test(f"{__name__}.ExampleTests.test_unexpected_success") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:3] == [ "u", "═" * 80, ] else: assert lines[1:3] == [ "u", "━" * 80, ] def test_unexpected_success_verbose(self): result = self.run_test( "-v", "2", f"{__name__}.ExampleTests.test_unexpected_success" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ ( "test_unexpected_success (tests.test_test.ExampleTests." + "test_unexpected_success) ... unexpected success" ), "", "═" * 80, ] else: assert lines[1:4] == [ ( "test_unexpected_success (tests.test_test.ExampleTests) " + "... unexpected success" ), "", "━" * 80, ] def test_debug_sql(self): result = self.run_test( "--debug-sql", f"{__name__}.ExampleTests.test_failure_sql_query" ) assert result.returncode == 1 lines = result.stderr.splitlines() assert "─ locals ─" in result.stderr assert lines[-10:-7] == [ "AssertionError: False is not true", "", "─" * 80, ] # https://docs.djangoproject.com/en/4.0/releases/4.0/#logging sql_line_re = r"\(\d.\d\d\d\) SELECT 1234, 5678; args=\(5678,\); alias=default" assert re.match(sql_line_re, lines[-7]) assert lines[-6:-4] == [ "", "━" * 80, ] def test_pdb(self): result = self.run_test( "--pdb", f"{__name__}.ExampleTests.test_failure", input="p 2 * 2\n", ) assert result.returncode == 1 lines = result.stdout.splitlines() expected = [ "Found 1 test(s).", "System check identified no issues (0 silenced).", "", "Opening PDB: AssertionError('1 != 2')", ] assert lines[: len(expected)] == expected assert lines[len(expected) + 1] == "-> self.assertEqual(1, 2)" assert lines[len(expected) + 2 : len(expected) + 4] == [ "(Pdb) 4", "(Pdb) ", ] def test_failure_django_assertion(self): "Django and Unit test modules are hidden in tracebacks." result = self.run_test(f"{__name__}.ExampleTests.test_failure_django_assertion") assert result.returncode == 1 assert "testPartExecutor" not in result.stderr assert "_callTestMethod" not in result.stderr assert 'self.assertURLEqual("/url/", "/test/")' in result.stderr assert 'self.assertURLEqual("/url/", "/test/")' in result.stderr assert result.stderr.count("─ locals ─") == 1 def test_buffer_pass(self): result = self.run_test("--buffer", f"{__name__}.ExampleTests.test_pass_output") assert result.returncode == 0 assert "This is some example output" not in result.stdout assert "This is some example output" not in result.stderr def test_buffer_stdout(self): result = self.run_test( "--buffer", f"{__name__}.ExampleTests.test_failure_stdout" ) assert result.returncode == 1 assert result.stderr.splitlines()[-10:-4] == [ "AssertionError: False is not true", "", "Stdout:", "This is some example output", "", "━" * 80, ] def test_buffer_stdout_no_newline(self): result = self.run_test( "--buffer", f"{__name__}.ExampleTests.test_failure_stdout_no_newline" ) assert result.returncode == 1 assert result.stderr.splitlines()[-10:-4] == [ "AssertionError: False is not true", "", "Stdout:", "This is some example output", "", "━" * 80, ] def test_buffer_stderr(self): result = self.run_test( "--buffer", f"{__name__}.ExampleTests.test_failure_stderr" ) assert result.returncode == 1 assert result.stderr.splitlines()[-10:-4] == [ "AssertionError: False is not true", "", "Stderr:", "This is some example output", "", "━" * 80, ] def test_buffer_stderr_no_newline(self): result = self.run_test( "--buffer", f"{__name__}.ExampleTests.test_failure_stderr_no_newline" ) assert result.returncode == 1 assert result.stderr.splitlines()[-10:-4] == [ "AssertionError: False is not true", "", "Stderr:", "This is some example output", "", "━" * 80, ] durations_test = pytest.mark.skipif( sys.version_info < (3, 12) or django.VERSION < (5, 0), reason="unittest --durations only on Python 3.12+ and Django 5.0+", ) @durations_test def test_durations_upstream_source(self): # RichTestRunner completely replaces _printDurations(), so check the # overriden function for changes that may need copying in. source = dedent( inspect.getsource( unittest.TextTestRunner._printDurations, # type: ignore[attr-defined] ) ) expected = dedent( """\ def _printDurations(self, result): if not result.collectedDurations: return ls = sorted(result.collectedDurations, key=lambda x: x[1], reverse=True) if self.durations > 0: ls = ls[:self.durations] self.stream.writeln("Slowest test durations") if hasattr(result, 'separator2'): self.stream.writeln(result.separator2) hidden = False for test, elapsed in ls: if self.verbosity < 2 and elapsed < 0.001: hidden = True continue self.stream.writeln("%-10s %s" % ("%.3fs" % elapsed, test)) if hidden: self.stream.writeln("\\n(durations < 0.001s were hidden; " "use -v to show these durations)") else: self.stream.writeln("") """ ) assert source == expected @durations_test def test_durations_none(self): result = self.run_test( "--durations", "10", f"{__name__}.ExampleTests.test_slow", "--tag", "nonexistent", ) assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[0:2] == [ "", "━" * 80, ] assert re.fullmatch(r"Ran 0 tests in \d\.\d\d\ds", lines[2]) assert lines[3:] == [ "", "NO TESTS RAN", ] @durations_test def test_durations_shown(self): result = self.run_test( "--durations", "10", f"{__name__}.ExampleTests.test_slow" ) assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[2:6] == [ " Slowest test durations ", "┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓", "┃ Duration ┃ Test ┃", "┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩", ] assert re.fullmatch( re.escape("│ ") + r"\d.\d\d\ds " + re.escape("│ test_slow (tests.test_test.ExampleTests.test_slow) │"), lines[6], ) assert lines[7:8] == [ "└──────────┴────────────────────────────────────────────────────┘", ] @durations_test def test_durations_all(self): result = self.run_test("--durations", "0", f"{__name__}.ExampleTests.test_slow") assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[2:6] == [ " Slowest test durations ", "┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓", "┃ Duration ┃ Test ┃", "┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩", ] assert re.fullmatch( re.escape("│ ") + r"\d.\d\d\ds " + re.escape("│ test_slow (tests.test_test.ExampleTests.test_slow) │"), lines[6], ) assert lines[7:8] == [ "└──────────┴────────────────────────────────────────────────────┘", ] @durations_test def test_durations_hidden(self): result = self.run_test( "--durations", "10", f"{__name__}.ExampleTests.test_pass" ) assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[2:4] == [ "Slowest test durations", "Durations < 0.001s were hidden. Use -v to show these durations.", ] @durations_test def test_durations_verbose(self): result = self.run_test( "--durations", "10", "-v", "2", f"{__name__}.ExampleTests.test_pass" ) assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[3:9] == [ " Slowest test durations ", "┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓", "┃ Duration ┃ Test ┃", "┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩", "│ 0.000s │ test_pass (tests.test_test.ExampleTests.test_pass) │", "└──────────┴────────────────────────────────────────────────────┘", ] @durations_test def test_durations_shown_hidden(self): result = self.run_test( "--durations", "10", f"{__name__}.ExampleTests.test_pass", f"{__name__}.ExampleTests.test_slow", ) assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[2:6] == [ " Slowest test durations ", "┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓", "┃ Duration ┃ Test ┃", "┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩", ] assert re.fullmatch( re.escape("│ ") + r"\d.\d\d\ds " + re.escape("│ test_slow (tests.test_test.ExampleTests.test_slow) │"), lines[6], ) assert lines[7:9] == [ "└──────────┴────────────────────────────────────────────────────┘", " Durations < 0.001s were hidden. Use -v to show these durations. ", ] sub_test_test = pytest.mark.skipif( sys.version_info < (3, 11), reason="addSubTest added in Python 3.11.", ) @sub_test_test def test_subtest_upstream_source(self): # RichTextTestResult completely replaces _addSubTest(), so check the # overriden function for changes that may need copying in. source = dedent( inspect.getsource( unittest.TextTestResult.addSubTest, ) ) expected = dedent( """\ def addSubTest(self, test, subtest, err): if err is not None: if self.showAll: if issubclass(err[0], subtest.failureException): self._write_status(subtest, "FAIL") else: self._write_status(subtest, "ERROR") elif self.dots: if issubclass(err[0], subtest.failureException): self.stream.write('F') else: self.stream.write('E') self.stream.flush() super(TextTestResult, self).addSubTest(test, subtest, err) """ ) assert source == expected @sub_test_test def test_write_status_upstream_source(self): # RichTextTestResult completely replaces _write_status(), so check the # overriden function for changes that may need copying in. source = dedent( inspect.getsource( unittest.TextTestResult._write_status, # type: ignore[attr-defined] ) ) expected = dedent( """\ def _write_status(self, test, status): is_subtest = isinstance(test, _SubTest) if is_subtest or self._newline: if not self._newline: self.stream.writeln() if is_subtest: self.stream.write(" ") self.stream.write(self.getDescription(test)) self.stream.write(" ... ") self.stream.writeln(status) self.stream.flush() self._newline = True """ ) assert source == expected def test_subtest(self): result = self.run_test(f"{__name__}.ExampleTests.test_subtest") assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[1] == "." def test_subtest_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_subtest") assert result.returncode == 0 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert ( lines[1] == "test_subtest (tests.test_test.ExampleTests.test_subtest) ... ok" ) else: assert lines[1] == "test_subtest (tests.test_test.ExampleTests) ... ok" def test_subtest_quiet(self): result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_subtest") assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[0] == "━" * 80 def test_subtest_quiet_fail(self): result = self.run_test( "-v", "0", f"{__name__}.ExampleTests.test_failure_subtest" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[:2] == [ "─" * 80, "FAIL: test_failure_subtest (tests.test_test.ExampleTests.test_failure_subtest) ", ] else: assert lines[:2] == [ "─" * 80, "FAIL: test_failure_subtest (tests.test_test.ExampleTests) (i=1)", ] def test_failure_subtest(self): result = self.run_test(f"{__name__}.ExampleTests.test_failure_subtest") assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "F" def test_failure_subtest_verbose(self): result = self.run_test( "-v", "2", f"{__name__}.ExampleTests.test_failure_subtest" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_failure_subtest (tests.test_test.ExampleTests.test_failure_subtest) ... ", " test_failure_subtest (tests.test_test.ExampleTests.test_failure_subtest) (i=1) ... FAIL", "", ] else: assert lines[1:4] == [ "test_failure_subtest (tests.test_test.ExampleTests) ... ", " test_failure_subtest (tests.test_test.ExampleTests) (i=1) ... FAIL", "", ] def test_subtest_skip(self): result = self.run_test(f"{__name__}.ExampleTests.test_skip_subtest") assert result.returncode == 0 lines = result.stderr.splitlines() assert lines[1] == "s" def test_subtest_skip_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_skip_subtest") assert result.returncode == 0 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_skip_subtest (tests.test_test.ExampleTests.test_skip_subtest) ... ", " test_skip_subtest (tests.test_test.ExampleTests.test_skip_subtest) (b=2) ... skipped 'skip'", "", ] else: assert lines[1:4] == [ "test_skip_subtest (tests.test_test.ExampleTests) ... ", " test_skip_subtest (tests.test_test.ExampleTests) (b=2) ... skipped 'skip'", "", ] def test_subtest_mixed(self): result = self.run_test(f"{__name__}.ExampleTests.test_mixed_subtest") assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "sFE" def test_subtest_mixed_verbose(self): result = self.run_test("-v", "2", f"{__name__}.ExampleTests.test_mixed_subtest") assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:6] == [ "test_mixed_subtest (tests.test_test.ExampleTests.test_mixed_subtest) ... ", " test_mixed_subtest (tests.test_test.ExampleTests.test_mixed_subtest) (b=2) ... skipped 'skip'", " test_mixed_subtest (tests.test_test.ExampleTests.test_mixed_subtest) (c=3) ... FAIL", " test_mixed_subtest (tests.test_test.ExampleTests.test_mixed_subtest) (d=4) ... ERROR", "", ] else: assert lines[1:6] == [ "test_mixed_subtest (tests.test_test.ExampleTests) ... ", " test_mixed_subtest (tests.test_test.ExampleTests) (b=2) ... skipped 'skip'", " test_mixed_subtest (tests.test_test.ExampleTests) (c=3) ... FAIL", " test_mixed_subtest (tests.test_test.ExampleTests) (d=4) ... ERROR", "", ] def test_tearDown_fail(self): result = self.run_test( f"{__name__}.TearDownFailTests.test_tearDownError_success" ) assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "F" result = self.run_test(f"{__name__}.TearDownFailTests.test_tearDownError_fail") assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "FF" result = self.run_test(f"{__name__}.TearDownFailTests.test_tearDownError_error") assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "EF" result = self.run_test(f"{__name__}.TearDownErrorTests.test_tearDownError_skip") assert result.returncode == 1 lines = result.stderr.splitlines() assert lines[1] == "sE" def test_tearDown_fail_verbose(self): result = self.run_test( "-v", "2", f"{__name__}.TearDownFailTests.test_tearDownError_success" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert ( lines[1] == "test_tearDownError_success (tests.test_test.TearDownFailTests.test_tearDownError_success) ... FAIL" ) else: assert ( lines[1] == "test_tearDownError_success (tests.test_test.TearDownFailTests) ... FAIL" ) result = self.run_test( "-v", "2", f"{__name__}.TearDownFailTests.test_tearDownError_fail" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_tearDownError_fail (tests.test_test.TearDownFailTests.test_tearDownError_fail) ... FAIL", "test_tearDownError_fail ", "(tests.test_test.TearDownFailTests.test_tearDownError_fail) ... FAIL", ] else: assert lines[1:3] == [ "test_tearDownError_fail (tests.test_test.TearDownFailTests) ... FAIL", "test_tearDownError_fail (tests.test_test.TearDownFailTests) ... FAIL", ] result = self.run_test( "-v", "2", f"{__name__}.TearDownFailTests.test_tearDownError_error" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_tearDownError_error (tests.test_test.TearDownFailTests.test_tearDownError_error) ... ERROR", "test_tearDownError_error ", "(tests.test_test.TearDownFailTests.test_tearDownError_error) ... FAIL", ] else: assert lines[1:3] == [ "test_tearDownError_error (tests.test_test.TearDownFailTests) ... ERROR", "test_tearDownError_error (tests.test_test.TearDownFailTests) ... FAIL", ] result = self.run_test( "-v", "2", f"{__name__}.TearDownErrorTests.test_tearDownError_skip" ) assert result.returncode == 1 lines = result.stderr.splitlines() if sys.version_info >= (3, 11): assert lines[1:4] == [ "test_tearDownError_skip (tests.test_test.TearDownErrorTests.test_tearDownError_skip) ... skipped 'skip'", "test_tearDownError_skip ", "(tests.test_test.TearDownErrorTests.test_tearDownError_skip) ... ERROR", ] else: assert lines[1:3] == [ "test_tearDownError_skip (tests.test_test.TearDownErrorTests) ... skipped 'skip'", "test_tearDownError_skip (tests.test_test.TearDownErrorTests) ... ERROR", ] django-rich-2.0.0/tests/testapp/000077500000000000000000000000001500736107700165265ustar00rootroot00000000000000django-rich-2.0.0/tests/testapp/__init__.py000066400000000000000000000001201500736107700206300ustar00rootroot00000000000000from __future__ import annotations import coverage coverage.process_startup() django-rich-2.0.0/tests/testapp/management/000077500000000000000000000000001500736107700206425ustar00rootroot00000000000000django-rich-2.0.0/tests/testapp/management/__init__.py000066400000000000000000000000001500736107700227410ustar00rootroot00000000000000django-rich-2.0.0/tests/testapp/management/commands/000077500000000000000000000000001500736107700224435ustar00rootroot00000000000000django-rich-2.0.0/tests/testapp/management/commands/__init__.py000066400000000000000000000000001500736107700245420ustar00rootroot00000000000000django-rich-2.0.0/tests/testapp/management/commands/example.py000066400000000000000000000003231500736107700244460ustar00rootroot00000000000000from __future__ import annotations from django_rich.management import RichCommand class Command(RichCommand): def handle(self, *args, **options): self.console.print("[bold red]Alert![/bold red]") django-rich-2.0.0/tox.ini000066400000000000000000000012351500736107700152200ustar00rootroot00000000000000[tox] requires = tox>=4.2 env_list = py313-django{52, 51} py312-django{52, 51, 50, 42} py311-django{52, 51, 50, 42} py310-django{52, 51, 50, 42} py39-django{42} [testenv] runner = uv-venv-lock-runner package = wheel wheel_build_env = .pkg set_env = PYTHONDEVMODE = 1 commands = python \ -W error::ResourceWarning \ -W error::DeprecationWarning \ -W error::PendingDeprecationWarning \ -m coverage run \ -m pytest {posargs:tests} dependency_groups = test django42: django42 django50: django50 django51: django51 django52: django52 [flake8] max-line-length = 88 extend-ignore = E203,E501 django-rich-2.0.0/uv.lock000066400000000000000000001673001500736107700152170ustar00rootroot00000000000000version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", "python_full_version < '3.10'", ] conflicts = [[ { package = "django-rich", group = "django42" }, { package = "django-rich", group = "django50" }, { package = "django-rich", group = "django51" }, { package = "django-rich", group = "django52" }, ]] [[package]] name = "asgiref" version = "3.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "coverage" version = "7.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload-time = "2025-03-30T20:36:45.376Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/78/01/1c5e6ee4ebaaa5e079db933a9a45f61172048c7efa06648445821a201084/coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", size = 211379, upload-time = "2025-03-30T20:34:53.904Z" }, { url = "https://files.pythonhosted.org/packages/e9/16/a463389f5ff916963471f7c13585e5f38c6814607306b3cb4d6b4cf13384/coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", size = 211814, upload-time = "2025-03-30T20:34:56.959Z" }, { url = "https://files.pythonhosted.org/packages/b8/b1/77062b0393f54d79064dfb72d2da402657d7c569cfbc724d56ac0f9c67ed/coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", size = 240937, upload-time = "2025-03-30T20:34:58.751Z" }, { url = "https://files.pythonhosted.org/packages/d7/54/c7b00a23150083c124e908c352db03bcd33375494a4beb0c6d79b35448b9/coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", size = 238849, upload-time = "2025-03-30T20:35:00.521Z" }, { url = "https://files.pythonhosted.org/packages/f7/ec/a6b7cfebd34e7b49f844788fda94713035372b5200c23088e3bbafb30970/coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", size = 239986, upload-time = "2025-03-30T20:35:02.307Z" }, { url = "https://files.pythonhosted.org/packages/21/8c/c965ecef8af54e6d9b11bfbba85d4f6a319399f5f724798498387f3209eb/coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", size = 239896, upload-time = "2025-03-30T20:35:04.141Z" }, { url = "https://files.pythonhosted.org/packages/40/83/070550273fb4c480efa8381735969cb403fa8fd1626d74865bfaf9e4d903/coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", size = 238613, upload-time = "2025-03-30T20:35:05.889Z" }, { url = "https://files.pythonhosted.org/packages/07/76/fbb2540495b01d996d38e9f8897b861afed356be01160ab4e25471f4fed1/coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", size = 238909, upload-time = "2025-03-30T20:35:07.76Z" }, { url = "https://files.pythonhosted.org/packages/a3/7e/76d604db640b7d4a86e5dd730b73e96e12a8185f22b5d0799025121f4dcb/coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", size = 213948, upload-time = "2025-03-30T20:35:09.144Z" }, { url = "https://files.pythonhosted.org/packages/5c/a7/f8ce4aafb4a12ab475b56c76a71a40f427740cf496c14e943ade72e25023/coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", size = 214844, upload-time = "2025-03-30T20:35:10.734Z" }, { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493, upload-time = "2025-03-30T20:35:12.286Z" }, { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921, upload-time = "2025-03-30T20:35:14.18Z" }, { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556, upload-time = "2025-03-30T20:35:15.616Z" }, { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245, upload-time = "2025-03-30T20:35:18.648Z" }, { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032, upload-time = "2025-03-30T20:35:20.131Z" }, { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679, upload-time = "2025-03-30T20:35:21.636Z" }, { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852, upload-time = "2025-03-30T20:35:23.525Z" }, { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389, upload-time = "2025-03-30T20:35:25.09Z" }, { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997, upload-time = "2025-03-30T20:35:26.914Z" }, { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911, upload-time = "2025-03-30T20:35:28.498Z" }, { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684, upload-time = "2025-03-30T20:35:29.959Z" }, { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935, upload-time = "2025-03-30T20:35:31.912Z" }, { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994, upload-time = "2025-03-30T20:35:33.455Z" }, { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885, upload-time = "2025-03-30T20:35:35.354Z" }, { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142, upload-time = "2025-03-30T20:35:37.121Z" }, { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906, upload-time = "2025-03-30T20:35:39.07Z" }, { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124, upload-time = "2025-03-30T20:35:40.598Z" }, { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317, upload-time = "2025-03-30T20:35:42.204Z" }, { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170, upload-time = "2025-03-30T20:35:44.216Z" }, { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969, upload-time = "2025-03-30T20:35:45.797Z" }, { url = "https://files.pythonhosted.org/packages/f3/21/87e9b97b568e223f3438d93072479c2f36cc9b3f6b9f7094b9d50232acc0/coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", size = 211708, upload-time = "2025-03-30T20:35:47.417Z" }, { url = "https://files.pythonhosted.org/packages/75/be/882d08b28a0d19c9c4c2e8a1c6ebe1f79c9c839eb46d4fca3bd3b34562b9/coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", size = 211981, upload-time = "2025-03-30T20:35:49.002Z" }, { url = "https://files.pythonhosted.org/packages/7a/1d/ce99612ebd58082fbe3f8c66f6d8d5694976c76a0d474503fa70633ec77f/coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", size = 245495, upload-time = "2025-03-30T20:35:51.073Z" }, { url = "https://files.pythonhosted.org/packages/dc/8d/6115abe97df98db6b2bd76aae395fcc941d039a7acd25f741312ced9a78f/coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", size = 242538, upload-time = "2025-03-30T20:35:52.941Z" }, { url = "https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", size = 244561, upload-time = "2025-03-30T20:35:54.658Z" }, { url = "https://files.pythonhosted.org/packages/22/70/c10c77cd77970ac965734fe3419f2c98665f6e982744a9bfb0e749d298f4/coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", size = 244633, upload-time = "2025-03-30T20:35:56.221Z" }, { url = "https://files.pythonhosted.org/packages/38/5a/4f7569d946a07c952688debee18c2bb9ab24f88027e3d71fd25dbc2f9dca/coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", size = 242712, upload-time = "2025-03-30T20:35:57.801Z" }, { url = "https://files.pythonhosted.org/packages/bb/a1/03a43b33f50475a632a91ea8c127f7e35e53786dbe6781c25f19fd5a65f8/coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", size = 244000, upload-time = "2025-03-30T20:35:59.378Z" }, { url = "https://files.pythonhosted.org/packages/6a/89/ab6c43b1788a3128e4d1b7b54214548dcad75a621f9d277b14d16a80d8a1/coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", size = 214195, upload-time = "2025-03-30T20:36:01.005Z" }, { url = "https://files.pythonhosted.org/packages/12/12/6bf5f9a8b063d116bac536a7fb594fc35cb04981654cccb4bbfea5dcdfa0/coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", size = 214998, upload-time = "2025-03-30T20:36:03.006Z" }, { url = "https://files.pythonhosted.org/packages/2a/e6/1e9df74ef7a1c983a9c7443dac8aac37a46f1939ae3499424622e72a6f78/coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", size = 212541, upload-time = "2025-03-30T20:36:04.638Z" }, { url = "https://files.pythonhosted.org/packages/04/51/c32174edb7ee49744e2e81c4b1414ac9df3dacfcb5b5f273b7f285ad43f6/coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", size = 212767, upload-time = "2025-03-30T20:36:06.503Z" }, { url = "https://files.pythonhosted.org/packages/e9/8f/f454cbdb5212f13f29d4a7983db69169f1937e869a5142bce983ded52162/coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", size = 256997, upload-time = "2025-03-30T20:36:08.137Z" }, { url = "https://files.pythonhosted.org/packages/e6/74/2bf9e78b321216d6ee90a81e5c22f912fc428442c830c4077b4a071db66f/coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", size = 252708, upload-time = "2025-03-30T20:36:09.781Z" }, { url = "https://files.pythonhosted.org/packages/92/4d/50d7eb1e9a6062bee6e2f92e78b0998848a972e9afad349b6cdde6fa9e32/coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", size = 255046, upload-time = "2025-03-30T20:36:11.409Z" }, { url = "https://files.pythonhosted.org/packages/40/9e/71fb4e7402a07c4198ab44fc564d09d7d0ffca46a9fb7b0a7b929e7641bd/coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", size = 256139, upload-time = "2025-03-30T20:36:13.86Z" }, { url = "https://files.pythonhosted.org/packages/49/1a/78d37f7a42b5beff027e807c2843185961fdae7fe23aad5a4837c93f9d25/coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", size = 254307, upload-time = "2025-03-30T20:36:16.074Z" }, { url = "https://files.pythonhosted.org/packages/58/e9/8fb8e0ff6bef5e170ee19d59ca694f9001b2ec085dc99b4f65c128bb3f9a/coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", size = 255116, upload-time = "2025-03-30T20:36:18.033Z" }, { url = "https://files.pythonhosted.org/packages/56/b0/d968ecdbe6fe0a863de7169bbe9e8a476868959f3af24981f6a10d2b6924/coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", size = 214909, upload-time = "2025-03-30T20:36:19.644Z" }, { url = "https://files.pythonhosted.org/packages/87/e9/d6b7ef9fecf42dfb418d93544af47c940aa83056c49e6021a564aafbc91f/coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", size = 216068, upload-time = "2025-03-30T20:36:21.282Z" }, { url = "https://files.pythonhosted.org/packages/60/0c/5da94be095239814bf2730a28cffbc48d6df4304e044f80d39e1ae581997/coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f", size = 211377, upload-time = "2025-03-30T20:36:23.298Z" }, { url = "https://files.pythonhosted.org/packages/d5/cb/b9e93ebf193a0bb89dbcd4f73d7b0e6ecb7c1b6c016671950e25f041835e/coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a", size = 211803, upload-time = "2025-03-30T20:36:25.74Z" }, { url = "https://files.pythonhosted.org/packages/78/1a/cdbfe9e1bb14d3afcaf6bb6e1b9ba76c72666e329cd06865bbd241efd652/coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82", size = 240561, upload-time = "2025-03-30T20:36:27.548Z" }, { url = "https://files.pythonhosted.org/packages/59/04/57f1223f26ac018d7ce791bfa65b0c29282de3e041c1cd3ed430cfeac5a5/coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814", size = 238488, upload-time = "2025-03-30T20:36:29.175Z" }, { url = "https://files.pythonhosted.org/packages/b7/b1/0f25516ae2a35e265868670384feebe64e7857d9cffeeb3887b0197e2ba2/coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c", size = 239589, upload-time = "2025-03-30T20:36:30.876Z" }, { url = "https://files.pythonhosted.org/packages/e0/a4/99d88baac0d1d5a46ceef2dd687aac08fffa8795e4c3e71b6f6c78e14482/coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd", size = 239366, upload-time = "2025-03-30T20:36:32.563Z" }, { url = "https://files.pythonhosted.org/packages/ea/9e/1db89e135feb827a868ed15f8fc857160757f9cab140ffee21342c783ceb/coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4", size = 237591, upload-time = "2025-03-30T20:36:34.721Z" }, { url = "https://files.pythonhosted.org/packages/1b/6d/ac4d6fdfd0e201bc82d1b08adfacb1e34b40d21a22cdd62cfaf3c1828566/coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899", size = 238572, upload-time = "2025-03-30T20:36:36.805Z" }, { url = "https://files.pythonhosted.org/packages/25/5e/917cbe617c230f7f1745b6a13e780a3a1cd1cf328dbcd0fd8d7ec52858cd/coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f", size = 213966, upload-time = "2025-03-30T20:36:38.551Z" }, { url = "https://files.pythonhosted.org/packages/bd/93/72b434fe550135869f9ea88dd36068af19afce666db576e059e75177e813/coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3", size = 214852, upload-time = "2025-03-30T20:36:40.209Z" }, { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443, upload-time = "2025-03-30T20:36:41.959Z" }, { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload-time = "2025-03-30T20:36:43.61Z" }, ] [package.optional-dependencies] toml = [ { name = "tomli", marker = "python_full_version <= '3.11' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, ] [[package]] name = "django" version = "4.2.21" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", "python_full_version < '3.10'", ] dependencies = [ { name = "asgiref" }, { name = "sqlparse" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c1/bb/2fad5edc1af2945cb499a2e322ac28e4714fc310bd5201ed1f5a9f73a342/django-4.2.21.tar.gz", hash = "sha256:b54ac28d6aa964fc7c2f7335138a54d78980232011e0cd2231d04eed393dcb0d", size = 10424638, upload-time = "2025-05-07T14:07:07.992Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/4f/aeaa3098da18b625ed672f3da6d1cd94e188d1b2cc27c2c841b2f9666282/django-4.2.21-py3-none-any.whl", hash = "sha256:1d658c7bf5d31c7d0cac1cab58bc1f822df89255080fec81909256c30e6180b3", size = 7993839, upload-time = "2025-05-07T14:07:01.318Z" }, ] [[package]] name = "django" version = "5.0.14" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ { name = "asgiref", marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "sqlparse", marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "tzdata", marker = "(python_full_version >= '3.10' and sys_platform == 'win32' and extra == 'group-11-django-rich-django50') or (python_full_version < '3.10' and extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (python_full_version < '3.10' and extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (python_full_version < '3.10' and extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (sys_platform != 'win32' and extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (sys_platform != 'win32' and extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (sys_platform != 'win32' and extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra != 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/cc0205045386b5be8eecb15a95f290383d103f0db5f7e34f93dcc340d5b0/Django-5.0.14.tar.gz", hash = "sha256:29019a5763dbd48da1720d687c3522ef40d1c61be6fb2fad27ed79e9f655bc11", size = 10644306, upload-time = "2025-04-02T11:24:41.396Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c0/93/eabde8789f41910845567ebbff5aacd52fd80e54c934ce15b83d5f552d2c/Django-5.0.14-py3-none-any.whl", hash = "sha256:e762bef8629ee704de215ebbd32062b84f4e56327eed412e5544f6f6eb1dfd74", size = 8185934, upload-time = "2025-04-02T11:24:36.888Z" }, ] [[package]] name = "django" version = "5.1.9" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ { name = "asgiref", marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52')" }, { name = "sqlparse", marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52')" }, { name = "tzdata", marker = "(python_full_version >= '3.10' and sys_platform == 'win32' and extra == 'group-11-django-rich-django51') or (python_full_version < '3.10' and extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (sys_platform != 'win32' and extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/10/08/2e6f05494b3fc0a3c53736846034f882b82ee6351791a7815bbb45715d79/django-5.1.9.tar.gz", hash = "sha256:565881bdd0eb67da36442e9ac788bda90275386b549070d70aee86327781a4fc", size = 10710887, upload-time = "2025-05-07T14:06:45.257Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e1/d1/d8b6b8250b84380d5a123e099ad3298a49407d81598faa13b43a2c6d96d7/django-5.1.9-py3-none-any.whl", hash = "sha256:2fd1d4a0a66a5ba702699eb692e75b0d828b73cc2f4e1fc4b6a854a918967411", size = 8277363, upload-time = "2025-05-07T14:06:37.426Z" }, ] [[package]] name = "django" version = "5.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ { name = "asgiref", marker = "python_full_version >= '3.10'" }, { name = "sqlparse", marker = "python_full_version >= '3.10'" }, { name = "tzdata", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/10/0d546258772b8f31398e67c85e52c66ebc2b13a647193c3eef8ee433f1a8/django-5.2.1.tar.gz", hash = "sha256:57fe1f1b59462caed092c80b3dd324fd92161b620d59a9ba9181c34746c97284", size = 10818735, upload-time = "2025-05-07T14:06:17.543Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/90/92/7448697b5838b3a1c6e1d2d6a673e908d0398e84dc4f803a2ce11e7ffc0f/django-5.2.1-py3-none-any.whl", hash = "sha256:a9b680e84f9a0e71da83e399f1e922e1ab37b2173ced046b541c72e1589a5961", size = 8301833, upload-time = "2025-05-07T14:06:10.955Z" }, ] [[package]] name = "django-rich" version = "2.0.0" source = { editable = "." } dependencies = [ { name = "django", version = "4.2.21", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or extra == 'group-11-django-rich-django42' or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "django", version = "5.0.14", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "django", version = "5.1.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.10' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52')" }, { name = "django", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.10' and extra != 'group-11-django-rich-django42' and extra != 'group-11-django-rich-django50' and extra != 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "rich" }, ] [package.dev-dependencies] django42 = [ { name = "django", version = "4.2.21", source = { registry = "https://pypi.org/simple" } }, ] django50 = [ { name = "django", version = "5.0.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] django51 = [ { name = "django", version = "5.1.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] django52 = [ { name = "django", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] test = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, { name = "pytest-django" }, { name = "pytest-randomly" }, { name = "rich" }, ] [package.metadata] requires-dist = [ { name = "django", specifier = ">=4.2" }, { name = "rich", specifier = ">=10" }, ] [package.metadata.requires-dev] django42 = [{ name = "django", marker = "python_full_version >= '3.8'", specifier = ">=4.2a1,<5" }] django50 = [{ name = "django", marker = "python_full_version >= '3.10'", specifier = ">=5.0a1,<5.1" }] django51 = [{ name = "django", marker = "python_full_version >= '3.10'", specifier = ">=5.1a1,<5.2" }] django52 = [{ name = "django", marker = "python_full_version >= '3.10'", specifier = ">=5.2a1,<6" }] test = [ { name = "coverage", extras = ["toml"] }, { name = "pytest" }, { name = "pytest-django" }, { name = "pytest-randomly" }, { name = "rich" }, ] [[package]] name = "exceptiongroup" version = "1.2.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, ] [[package]] name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp", marker = "python_full_version < '3.10' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "markdown-it-py" version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, ] [[package]] name = "pytest" version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] [[package]] name = "pytest-django" version = "4.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/fb/55d580352db26eb3d59ad50c64321ddfe228d3d8ac107db05387a2fadf3a/pytest_django-4.11.1.tar.gz", hash = "sha256:a949141a1ee103cb0e7a20f1451d355f83f5e4a5d07bdd4dcfdd1fd0ff227991", size = 86202, upload-time = "2025-04-03T18:56:09.338Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/be/ac/bd0608d229ec808e51a21044f3f2f27b9a37e7a0ebaca7247882e67876af/pytest_django-4.11.1-py3-none-any.whl", hash = "sha256:1b63773f648aa3d8541000c26929c1ea63934be1cfa674c76436966d73fe6a10", size = 25281, upload-time = "2025-04-03T18:56:07.678Z" }, ] [[package]] name = "pytest-randomly" version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata", marker = "python_full_version < '3.10' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload-time = "2024-10-25T15:45:32.78Z" }, ] [[package]] name = "rich" version = "14.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django50') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django42' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django51') or (extra == 'group-11-django-rich-django50' and extra == 'group-11-django-rich-django52') or (extra == 'group-11-django-rich-django51' and extra == 'group-11-django-rich-django52')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, ] [[package]] name = "sqlparse" version = "0.5.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] name = "typing-extensions" version = "4.13.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, ] [[package]] name = "tzdata" version = "2025.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, ] [[package]] name = "zipp" version = "3.21.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545, upload-time = "2024-11-10T15:05:20.202Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630, upload-time = "2024-11-10T15:05:19.275Z" }, ]