pax_global_header00006660000000000000000000000064147356104260014522gustar00rootroot0000000000000052 comment=62eccce8d002f8ea4b72231d7d3adcf305465fec django-pgbulk-3.2.2/000077500000000000000000000000001473561042600142525ustar00rootroot00000000000000django-pgbulk-3.2.2/.circleci/000077500000000000000000000000001473561042600161055ustar00rootroot00000000000000django-pgbulk-3.2.2/.circleci/config.yml000066400000000000000000000051351473561042600201010ustar00rootroot00000000000000version: 2.1 orbs: ambitioneng: executors: python: working_directory: /code docker: - image: opus10/circleci-python-library:2024-10-26 environment: # Ensure makefile commands are not wrapped in "docker compose run" EXEC_WRAPPER: '' DATABASE_URL: postgres://root@localhost/circle_test?sslmode=disable - image: cimg/postgres:<> environment: POSTGRES_USER: root POSTGRES_DB: circle_test POSTGRES_PASSWORD: password parameters: pg_version: type: "string" default: "14.4" commands: test: steps: - checkout - restore_cache: key: v5-{{ checksum "poetry.lock" }} - run: make dependencies - run: make full-test-suite - save_cache: key: v5-{{ checksum "poetry.lock" }} paths: - /home/circleci/.cache/pypoetry/ - /code/.venv - /code/.tox jobs: test_pg_min: executor: name: ambitioneng/python pg_version: "13.16" steps: - ambitioneng/test test_pg_max: executor: name: ambitioneng/python pg_version: "17.0" steps: - ambitioneng/test lint: executor: ambitioneng/python steps: - checkout - restore_cache: key: v5-{{ checksum "poetry.lock" }} - run: make dependencies - run: make lint type_check: executor: ambitioneng/python steps: - checkout - restore_cache: key: v5-{{ checksum "poetry.lock" }} - run: make dependencies - run: make type-check deploy: executor: ambitioneng/python steps: - checkout - run: ssh-add -D - restore_cache: key: v5-{{ checksum "poetry.lock" }} - run: make dependencies - run: poetry run python devops.py deploy workflows: version: 2 on_commit: jobs: - test_pg_min: filters: tags: only: /.*/ - test_pg_max: filters: tags: only: /.*/ - lint: filters: tags: only: /.*/ - type_check: filters: tags: only: /.*/ - deploy: context: python-library requires: - test_pg_min - test_pg_max - lint - type_check filters: branches: ignore: /.*/ tags: only: /.*/ django-pgbulk-3.2.2/.editorconfig000066400000000000000000000003221473561042600167240ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.{yaml,yml}] indent_size = 2 [makefile] indent_style = tab django-pgbulk-3.2.2/.gitignore000066400000000000000000000135571473561042600162550ustar00rootroot00000000000000 # Created by https://www.gitignore.io/api/vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode # Edit at https://www.gitignore.io/?templates=vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode ### Django ### *.log *.pot *.pyc __pycache__/ local_settings.py db.sqlite3 media # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ # in your Git repository. Update and uncomment the following line accordingly. # /staticfiles/ ### Django.Python Stack ### # Byte-compiled / optimized / DLL files *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo # Django stuff: db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ ### ElasticBeanstalk ### .elasticbeanstalk/ ### KomodoEdit ### *.komodoproject .komodotools ### OSX ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### PyCharm ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf # Generated files .idea/**/contentModel.xml # Sensitive or high-churn files .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.local.xml .idea/**/sqlDataSources.xml .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml # Gradle .idea/**/gradle.xml .idea/**/libraries # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/modules.xml # .idea/*.iml # .idea/modules # *.iml # *.ipr # CMake cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml # File-based project format *.iws # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser ### PyCharm Patch ### # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 # *.iml # modules.xml # .idea/misc.xml # *.ipr # Sonarlint plugin .idea/sonarlint ### Python ### # Byte-compiled / optimized / DLL files # C extensions # Distribution / packaging # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. # Installer logs # Unit test / coverage reports # Translations # Django stuff: # Flask stuff: # Scrapy stuff: # Sphinx documentation # PyBuilder # Jupyter Notebook # IPython # pyenv # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. # celery beat schedule file # SageMath parsed files # Environments # Spyder project settings # Rope project settings # mkdocs documentation # mypy # Pyre type checker ### Vim ### # Swap [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] # Session Session.vim Sessionx.vim # Temporary .netrwhist *~ # Auto-generated tag files tags # Persistent undo [._]*.un~ ### VisualStudioCode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json ### VisualStudioCode Patch ### # Ignore all local history of files .history # End of https://www.gitignore.io/api/vim,osx,python,django,pycharm,komodoedit,elasticbeanstalk,visualstudiocode # Ignore custom Docker compose DB data .db # Ignore local poetry settings poetry.toml # Ignore PyCharm idea folder .idea django-pgbulk-3.2.2/.readthedocs.yml000066400000000000000000000003051473561042600173360ustar00rootroot00000000000000version: 2 build: os: ubuntu-22.04 tools: python: "3.9" mkdocs: configuration: mkdocs.yml fail_on_warning: false formats: all python: install: - requirements: docs/requirements.txt django-pgbulk-3.2.2/CHANGELOG.md000066400000000000000000000217051473561042600160700ustar00rootroot00000000000000# Changelog ## 3.2.2 (2025-01-02) #### Changes - Use `cursor.connection` in psycopg2 to avoid issues caused by agents like NewRelic that wrap cursor objects by [@wesleykendall](https://github.com/wesleykendall) in [#51](https://github.com/AmbitionEng/django-pgbulk/pull/51). ## 3.2.1 (2024-12-15) #### Changes - Changed project ownership to `AmbitionEng` by [@wesleykendall](https://github.com/wesleykendall) in [#50](https://github.com/AmbitionEng/django-pgbulk/pull/50). ## 3.2.0 #### Changes - Added Python 3.13 support, dropped Python 3.8. Added Postgres17 support by [@wesleykendall](https://github.com/wesleykendall) in [#49](https://github.com/Opus10/django-pgbulk/pull/49). ## 3.1.0 #### Feature - Add support for db defaults in `pgbulk.upsert` by [@max-muoto](https://github.com/max-muoto) in [#42](https://github.com/Opus10/django-pgbulk/pull/42). - Support binary mode for `pgbulk.copy` by [@max-muoto](https://github.com/max-muoto) in [#45](https://github.com/Opus10/django-pgbulk/pull/45) ## 3.0.2 #### Trivial - Remove elipses defaults on overloads to avoid incorrect resolution by [@max-muoto](https://github.com/max-muoto) in [#41](https://github.com/Opus10/django-pgbulk/pull/41/). ## 3.0.1 #### Trivial - Add overloads on `upsert`, `aupsert`, `update`, and `aupdate` to improve type-checking on `returning=...` by [@max-muoto](https://github.com/max-muoto) in [#40](https://github.com/Opus10/django-pgbulk/pull/40/). ## 3.0.0 #### Breaking Changes - The `redundant_updates` flag for `pgbulk.upsert` was renamed to `ignore_unchanged`, and the default behavior was flipped by [@wesleykendall](https://github.com/wesleykendall) in [#38](https://github.com/Opus10/django-pgbulk/pull/38). Unlike before, unchanged rows are *not* ignored by default. See [the pull request](https://github.com/Opus10/django-pgbulk/pull/38) for a guide on how to update invocations from version 2. #### Features - Support update expressions, `returning`, and `ignore_unchanged` in `pgbulk.update` by [@wesleykendall](https://github.com/wesleykendall) in [#38](https://github.com/Opus10/django-pgbulk/pull/38) `pgbulk.update`'s interface has reached feature parity with `pgbulk.upsert`, allowing for returning results, ignoring unchanged rows from being updated, and bulk updates with expressions. - New `pgbulk.copy` function that leverages `COPY ... FROM` by [@wesleykendall](https://github.com/wesleykendall) in [#39](https://github.com/Opus10/django-pgbulk/pull/39) `pgbulk.copy` wraps Postgres's `COPY ... FROM` to insert data. Can be dramatically faster than Django's `bulk_create`. #### Changes - Django 5.1 compatibilty, dropped Django 3.2 / Postgres 12 support by [@wesleykendall](https://github.com/wesleykendall) in [#37](https://github.com/Opus10/django-pgbulk/pull/37). ## 2.5.0 (2024-08-09) #### Feature - Fix typing errors allowing for strict type-safety with Pyright. [Maxwell Muoto, 8158596] ## 2.4.0 (2024-04-24) #### Bug - Fix operations on virtual and generated fields. [Darlin Alberto, 852a492] Using non-concrete fields such as django-hashids HashidsField and the new Generatedfield in Django 5 previously produced errors during upsert and update operations. These fields are now fully supported. #### Trivial - Update with the latest Python library template. [Wesley Kendall, 6a00a64] ## 2.3.1 (2024-04-06) #### Trivial - Fix ReadTheDocs builds. [Wesley Kendall, 93965a9] ## 2.3.0 (2023-12-27) #### Feature - Add `exclude` arguments to `pgbulk.upsert` and `pgbulk.update`. [Maxwell Muoto, cde5904] Add `exclude` arguments to `pgbulk.upsert` and `pgbulk.update`. Users can now use `exclude=["field_name"]` to exclude fields for updating or upserting data. ## 2.2.0 (2023-11-26) #### Feature - Django 5.0 compatibility [Wesley Kendall, e7848ed] Support and test against Django 5 with psycopg2 and psycopg3. ## 2.1.1 (2023-11-23) #### Trivial - Add py.typed file, fix typing issues [Maxwell Muoto, 76f6e77] ## 2.1.0 (2023-11-03) #### Bug - Allow updates on custom primary key fields [Wesley Kendall, 4dbfb1c] `pgbulk.update` would fail on models with custom primary key fields when no `update_fields` argument was supplied. This has been fixed. ## 2.0.4 (2023-10-10) #### Trivial - Improve base type annotations, avoid type annotations in comments [Maxwell Muoto, 862e253] ## 2.0.3 (2023-10-09) #### Trivial - Added Opus10 branding to docs [Wesley Kendall, c2f9d18] ## 2.0.2 (2023-10-08) #### Trivial - Add additional docs and notes around async usaged and model signals. [Wesley Kendall, 4cce843] ## 2.0.1 (2023-10-08) #### Trivial - Fix release notes [Wesley Kendall, 8a88b7a] ## 2.0.0 (2023-10-08) #### Api-Break - Python 3.12 / async support, dropping of `pgbulk.sync` and `return_untouched` [Wesley Kendall, de70607] This version of `django-pgbulk` breaks the API in the following manner: - `pgbulk.upsert` no longer supports the `return_untouched` argument. It had race conditions and will only be brought back if those race conditions can be addressed. - `pgbulk.upsert`'s `ignore_duplicate_updates` was renamed to `redundant_updates`. The default functionality is still the same, but the argument now has the opposite meaning. - `pgbulk.sync` was dropped since it relied on the `return_untouched` argument. This release also includes the following changes: - Python 3.12 is supported and Python 3.7 is dropped - Postgres 16 support - Async-compatible `pgbulk.aupsert` and `pgbulk.aupdate` were added - New documentation theme and formatting with Material for Mkdocs - Type annotations for public functions and classes ## 1.4.0 (2023-06-08) #### Feature - Added Python 3.11, Django 4.2, and Psycopg 3 support [Wesley Kendall, f606b0b] Adds Python 3.11, Django 4.2, and Psycopg 3 support along with tests for multiple Postgres versions. Drops support for Django 2.2. ## 1.3.0 (2022-12-12) #### Feature - Sort bulk update objects [Wesley Kendall, f766617] Objects passed to ``pgbulk.update`` are now sorted to reduce the likelihood of a deadlock when executed concurrently. #### Trivial - Updated with latest Python template [Wesley Kendall, 9652cd2] - Updated with latest Django template [Wesley Kendall, 6ef27e6] ## 1.2.6 (2022-08-26) #### Trivial - Test against Django 4.1 and other CI improvements [Wes Kendall, 9eedff4] ## 1.2.5 (2022-08-24) #### Trivial - Fix ReadTheDocs builds [Wes Kendall, 15832a5] ## 1.2.4 (2022-08-20) #### Trivial - Updated with latest Django template [Wes Kendall, 4e9e095] ## 1.2.3 (2022-08-20) #### Trivial - Fix release note rendering and code formatting changes [Wes Kendall, 94f1192] ## 1.2.2 (2022-08-17) #### Trivial - README and intro documentation fix [Wes Kendall, e75930e] ## 1.2.1 (2022-07-31) #### Trivial - Updated with latest Django template, fixing doc builds [Wes Kendall, c3ed424] ## 1.2.0 (2022-03-14) #### Feature - Handle func-based fields and allow expressions in upserts [Wes Kendall, 64458c5] ``pgbulk.upsert`` allows users to provide a ``pgbulk.UpdateField`` object to the ``update_fields`` argument, allowing a users to specify an expression that happens if an update occurs. This allows, for example, a user to do ``models.F('my_field') + 1`` and increment integer fields in a ``pgbulk.upsert``. Along with this, fields that cast to ``Func`` and other expressions are properly handled during upsert. ## 1.1.1 (2022-03-14) #### Trivial - Updates to latest template, dropping py3.6 support and adding Django4 support [Wes Kendall, 35a04b0] ## 1.1.0 (2022-01-08) #### Bug - Fix error when upserting custom AutoFields [Wes Kendall, 114eb45] ``upsert()`` previously errored when using a custom auto-incrementing field. This has been tested and fixed. ## 1.0.2 (2021-06-06) #### Trivial - Updated with the latest Django template [Wes Kendall, 71a2678] ## 1.0.1 (2020-06-29) #### Trivial - Update with the latest public django app template. [Wes Kendall, 271b456] ## 1.0.0 (2020-06-27) #### Api-Break - Initial release of django-pgbulk. [Wes Kendall, 7070a26] The initial release of django-pgbulk includes three functions for bulk operations in postgres: 1. ``pgbulk.update`` - For updating a list of models in bulk. Although Django provides a ``bulk_update`` in 2.2, it performs individual updates for every row and does not perform a native bulk update. 2. ``pgbulk.upsert`` - For doing a bulk update or insert. This function uses postgres ``UPDATE ON CONFLICT`` syntax to perform an atomic upsert operation. There are several options to this function that allow the user to avoid touching rows if they result in a duplicate update, along with returning which rows were updated, created, or untouched. 3. ``pgbulk.sync`` - For syncing a list of models with a table. Does a bulk upsert and also deletes any rows in the source queryset that were not part of the input data. django-pgbulk-3.2.2/CONTRIBUTING.md000066400000000000000000000025271473561042600165110ustar00rootroot00000000000000# Contributing Guide This project was created using footing. For more information about footing, go to the [footing docs](https://github.com/AmbitionEng/footing). ## Setup Set up your development environment with: git clone git@github.com:AmbitionEng/django-pgbulk.git cd django-pgbulk make docker-setup `make docker-setup` will set up a development environment managed by Docker. Install docker [here](https://www.docker.com/get-started) and be sure it is running when executing any of the commands below. If you prefer a native development environment, `make conda-setup` will set up a development environment managed by [Conda](https://conda.io). Dependent services, such as databases, must be ran manually. ## Testing and Validation Run the tests on one Python version with: make test Run the full test suite against all supported Python versions with: make full-test-suite Validate the code with: make lint If your code fails the linter checks, fix common errors with: make lint-fix ## Documentation [Mkdocs Material](https://squidfunk.github.io/mkdocs-material/) documentation can be built with: make docs A shortcut for serving them is: make docs-serve ## Releases and Versioning The version number and release notes are manually updated by the maintainer during the release process. Do not edit these.django-pgbulk-3.2.2/LICENSE000066400000000000000000000026621473561042600152650ustar00rootroot00000000000000Copyright (c) 2025, Ambition All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AMBITION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-pgbulk-3.2.2/Makefile000066400000000000000000000111551473561042600157150ustar00rootroot00000000000000# Makefile for packaging and testing django-pgbulk # # This Makefile has the following targets: # # setup - Sets up the development environment # dependencies - Installs dependencies # docs - Build documentation # docs-serve - Serve documentation # lint - Run code linting and static checks # lint-fix - Fix common linting errors # type-check - Run Pyright type-checking # test - Run tests using pytest # full-test-suite - Run full test suite using tox # shell - Run a shell in a virtualenv # docker-teardown - Spin down docker resources OS = $(shell uname -s) PACKAGE_NAME=django-pgbulk MODULE_NAME=pgbulk SHELL=bash ifeq (${OS}, Linux) DOCKER_CMD?=sudo docker DOCKER_RUN_ARGS?=-v /home:/home -v $(shell pwd):/code -e EXEC_WRAPPER="" -u "$(shell id -u):$(shell id -g)" -v /etc/passwd:/etc/passwd # The user can be passed to docker exec commands in Linux. # For example, "make shell user=root" for access to apt-get commands user?=$(shell id -u) group?=$(shell id ${user} -u) EXEC_WRAPPER?=$(DOCKER_CMD) exec --user="$(user):$(group)" -it $(PACKAGE_NAME) else ifeq (${OS}, Darwin) DOCKER_CMD?=docker DOCKER_RUN_ARGS?=-v ~/:/home/circleci -v $(shell pwd):/code -e EXEC_WRAPPER="" EXEC_WRAPPER?=$(DOCKER_CMD) exec -it $(PACKAGE_NAME) endif # Docker run mounts the local code directory, SSH (for git), and global git config information DOCKER_RUN_CMD?=$(DOCKER_CMD) compose run --name $(PACKAGE_NAME) $(DOCKER_RUN_ARGS) -d app # Print usage of main targets when user types "make" or "make help" .PHONY: help help: ifndef run @echo "Please choose one of the following targets: \n"\ " docker-setup: Setup Docker development environment\n"\ " conda-setup: Setup Conda development environment\n"\ " lock: Lock dependencies\n"\ " dependencies: Install dependencies\n"\ " shell: Start a shell\n"\ " test: Run tests\n"\ " tox: Run tests against all versions of Python\n"\ " lint: Run code linting and static checks\n"\ " lint-fix: Fix common linting errors\n"\ " type-check: Run Pyright type-checking\n"\ " docs: Build documentation\n"\ " docs-serve: Serve documentation\n"\ " docker-teardown: Spin down docker resources\n"\ "\n"\ "View the Makefile for more documentation" @exit 2 else $(EXEC_WRAPPER) $(run) endif # Pull the latest container and start a detached run .PHONY: docker-start docker-start: $(DOCKER_CMD) compose pull $(DOCKER_RUN_CMD) # Lock dependencies .PHONY: lock lock: $(EXEC_WRAPPER) poetry lock --no-update $(EXEC_WRAPPER) poetry export --with dev --without-hashes -f requirements.txt > docs/requirements.txt # Install dependencies .PHONY: dependencies dependencies: $(EXEC_WRAPPER) poetry install --no-ansi # Sets up the local database .PHONY: db-setup db-setup: -psql postgres -c "CREATE USER postgres;" -psql postgres -c "ALTER USER postgres SUPERUSER;" -psql postgres -c "CREATE DATABASE ${MODULE_NAME}_local OWNER postgres;" -psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE ${MODULE_NAME}_local to postgres;" $(EXEC_WRAPPER) python manage.py migrate # Sets up a conda development environment .PHONY: conda-create conda-create: -conda env create -f environment.yml -y $(EXEC_WRAPPER) poetry config virtualenvs.create false --local # Sets up a Conda development environment .PHONY: conda-setup conda-setup: EXEC_WRAPPER=conda run -n ${PACKAGE_NAME} --no-capture-output conda-setup: conda-create lock dependencies db-setup # Sets up a Docker development environment .PHONY: docker-setup docker-setup: docker-teardown docker-start lock dependencies # Spin down docker resources .PHONY: docker-teardown docker-teardown: $(DOCKER_CMD) compose down --remove-orphans # Run a shell .PHONY: shell shell: $(EXEC_WRAPPER) /bin/bash # Run pytest .PHONY: test test: $(EXEC_WRAPPER) pytest # Run full test suite .PHONY: full-test-suite full-test-suite: $(EXEC_WRAPPER) tox # Build documentation .PHONY: docs docs: $(EXEC_WRAPPER) mkdocs build -s # Serve documentation .PHONY: docs-serve docs-serve: $(EXEC_WRAPPER) mkdocs serve # Run code linting and static analysis. Ensure docs can be built .PHONY: lint lint: $(EXEC_WRAPPER) ruff format . --check $(EXEC_WRAPPER) ruff check ${MODULE_NAME} $(EXEC_WRAPPER) bash -c 'make docs' $(EXEC_WRAPPER) diff <(poetry export --with dev --without-hashes -f requirements.txt) docs/requirements.txt >/dev/null 2>&1 || exit 1 # Fix common linting errors .PHONY: lint-fix lint-fix: $(EXEC_WRAPPER) ruff format . $(EXEC_WRAPPER) ruff check ${MODULE_NAME} --fix # Run Pyright type-checking .PHONY: type-check type-check: $(EXEC_WRAPPER) pyright $(MODULE_NAME) django-pgbulk-3.2.2/README.md000066400000000000000000000050041473561042600155300ustar00rootroot00000000000000# django-pgbulk `django-pgbulk` provides functions for doing native Postgres bulk upserts (i.e. [UPDATE ON CONFLICT](https://www.postgresql.org/docs/current/sql-insert.html)), bulk updates, and [COPY FROM](https://www.postgresql.org/docs/current/sql-copy.html). Bulk upserts can distinguish between updated/created rows and ignore unchanged updates. Bulk updates are true bulk updates, unlike Django's [bulk_update](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#bulk-update) which can still suffer from *O(N)* queries and can create poor locking scenarios. Bulk copies can significantly speed-up bulk inserts, sometimes by an order of magnitude over Django's `bulk_create`. ## Quick Start ### Examples #### Update or insert rows ```python import pgbulk pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], # These are the fields that identify the uniqueness constraint. ["int_field"], # These are the fields that will be updated if the row already # exists. If not provided, all fields will be updated ["some_attr"] ) ``` #### Bulk update rows ```python import pgbulk pgbulk.update( MyModel, [ MyModel(id=1, some_attr='some_val1'), MyModel(id=2, some_attr='some_val2') ], # These are the fields that will be updated. If not provided, # all fields will be updated ['some_attr'] ) ``` #### Copy rows into a table ```python import pgbulk pgbulk.copy( MyModel, # Insert these rows using COPY FROM [ MyModel(id=1, some_attr='some_val1'), MyModel(id=2, some_attr='some_val2') ], ) ``` ### Advanced Features Here are some advanced features at a glance: - `pgbulk.upsert` can categorize which rows were inserted or updated. - `pgbulk.upsert` and `pgbulk.update` can ignore updating unchanged fields. - `pgbulk.upsert` and `pgbulk.update` can use expressions in updates. ## Documentation [View the django-pgbulk docs here](https://django-pgbulk.readthedocs.io/) for more examples. ## Compatibility `django-pgbulk` is compatible with Python 3.9 - 3.13, Django 4.2 - 5.1, Psycopg 2 - 3, and Postgres 13 - 17. ## Installation Install `django-pgbulk` with: pip3 install django-pgbulk ## Contributing Guide For information on setting up django-pgbulk for development and contributing changes, view [CONTRIBUTING.md](CONTRIBUTING.md). ## Creators - [Wes Kendall](https://github.com/wesleykendall) ## Other Contributors - @max-muoto - @dalberto django-pgbulk-3.2.2/devops.py000066400000000000000000000032561473561042600161320ustar00rootroot00000000000000#!/usr/bin/env python3 """ Devops functions for this package. Includes functions for automated package deployment, changelog generation, and changelog checking. This script is generated by the template at https://github.com/AmbitionEng/python-library-template Do not change this script! Any fixes or updates to this script should be made to https://github.com/AmbitionEng/python-library-template """ import os import subprocess import sys from typing import IO, Any, TypeAlias, Union File: TypeAlias = Union[IO[Any], int, None] def _shell( cmd: str, check: bool = True, stdin: File = None, stdout: File = None, stderr: File = None, ): # pragma: no cover """Runs a subprocess shell with check=True by default""" return subprocess.run(cmd, shell=True, check=check, stdin=stdin, stdout=stdout, stderr=stderr) def _publish_to_pypi() -> None: """ Uses poetry to publish to pypi """ if "PYPI_USERNAME" not in os.environ or "PYPI_PASSWORD" not in os.environ: raise RuntimeError("Must set PYPI_USERNAME and PYPI_PASSWORD env vars") _shell("poetry config http-basic.pypi ${PYPI_USERNAME} ${PYPI_PASSWORD}") _shell("poetry build") _shell("poetry publish -vvv -n", stdout=subprocess.PIPE) def deploy() -> None: """Deploys the package and uploads documentation.""" # Ensure proper environment if not os.environ.get("CIRCLECI"): # pragma: no cover raise RuntimeError("Must be on CircleCI to run this script") _publish_to_pypi() print("Deployment complete.") if __name__ == "__main__": if sys.argv[-1] == "deploy": deploy() else: raise RuntimeError(f'Invalid subcommand "{sys.argv[-1]}"') django-pgbulk-3.2.2/docker-compose.yml000066400000000000000000000006071473561042600177120ustar00rootroot00000000000000version: "3.3" services: db: image: cimg/postgres:14.4 volumes: - ./.db:/var/lib/postgresql/data environment: - POSTGRES_NAME=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres app: image: opus10/circleci-python-library environment: - DATABASE_URL=postgres://postgres:postgres@db:5432/postgres depends_on: - db django-pgbulk-3.2.2/docs/000077500000000000000000000000001473561042600152025ustar00rootroot00000000000000django-pgbulk-3.2.2/docs/contributing.md000066400000000000000000000000301473561042600202240ustar00rootroot00000000000000--8<-- "CONTRIBUTING.md"django-pgbulk-3.2.2/docs/css/000077500000000000000000000000001473561042600157725ustar00rootroot00000000000000django-pgbulk-3.2.2/docs/css/mkdocs-material.css000066400000000000000000000006241473561042600215620ustar00rootroot00000000000000.md-typeset__table { min-width: 100%; } .md-typeset table:not([class]) { display: table; } :root { --md-primary-fg-color: #1d1f29; --md-primary-fg-color--light: #1d1f29; --md-primary-fg-color--dark: #1d1f29; } .md-content { --md-typeset-a-color: #00bc70; } .md-footer { background-color: #1d1f29; } .md-footer-meta { background-color: #1d1f29; } readthedocs-flyout { display: none; }django-pgbulk-3.2.2/docs/css/mkdocstrings.css000066400000000000000000000001651473561042600212150ustar00rootroot00000000000000div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid var(--md-typeset-table-color); }django-pgbulk-3.2.2/docs/guide.md000066400000000000000000000131441473561042600166240ustar00rootroot00000000000000# User Guide `django-pgbulk` comes with the following functions: - Use [pgbulk.upsert][] to do an `INSERT ON CONFLICT` statement. - Use [pgbulk.update][] to do a bulk `UPDATE` statement. - Use [pgbulk.copy][] to do a `COPY FROM` statement. - Use [pgbulk.aupsert][], [pgbulk.aupdate][], or [pgbulk.acopy][] for async versions. Below we show examples and advanced functionality. ## Using `pgbulk.upsert` [pgbulk.upsert][] allows for updating or inserting rows atomically and returning results based on inserts or updates. Update fields, returned values, and ignoring unchanged rows can be configured. See [the Postgres INSERT docs](https://www.postgresql.org/docs/current/sql-insert.html) for more information on how `ON CONFLICT` works. #### A basic bulk upsert on a model ```python import pgbulk pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], # These are the fields that identify the uniqueness constraint. ["int_field"], # These are the fields that will be updated if the row already # exists. If not provided, all fields will be updated ["some_attr"] ) ``` #### Return the results of an upsert ```python results = pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], ["int_field"], ["some_attr"], # `True` will return all columns. One can also explicitly # list which columns will be returned returning=True ) # Print which results were created print(results.created) # Print which results were updated. # By default, if an update results in no changes, it will not # be updated and will not be returned. print(results.updated) ``` #### Use an expression for updates In this example, we increment `some_int_field` by one whenever an update happens. Otherwise it defaults to zero: ```python pgbulk.upsert( MyModel, [ MyModel(some_int_field=0, some_key="a"), MyModel(some_int_field=0, some_key="b") ], ["some_key"], [ # Use UpdateField to specify an expression for the update. pgbulk.UpdateField( "some_int_field", expression=models.F("some_int_field") + 1 ) ], ) ``` #### Ignore updates to unchanged rows ```python pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], ["int_field"], ["some_attr"], ignore_unchanged=True ) ``` !!! warning Triggers and auto-generated fields not in the update won't be applied. Unchanged rows also won't be returned if using `returning=True`. ## Using `pgbulk.update` [pgbulk.update][] issues updates to multiple rows with an `UPDATE SET ... FROM VALUES` statement. Update fields, returned values, and ignoring unchanged rows can be configured. #### Update an attribute of multiple models in bulk ```python import pgbulk pgbulk.update( MyModel, [ MyModel(id=1, some_attr='some_val1'), MyModel(id=2, some_attr='some_val2') ], # These are the fields that will be updated. If not provided, # all fields will be updated ['some_attr'] ) ``` #### Use an expression in an update In the example, we increment `some_int_field` by one: ```python pgbulk.update( MyModel, [ MyModel(some_int_field=0, some_key="a"), MyModel(some_int_field=0, some_key="b") ], [ # Use UpdateField to specify an expression for the update. pgbulk.UpdateField( "some_int_field", expression=models.F("some_int_field") + 1 ) ], ) ``` #### Return the results of an update ```python results = pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], ["int_field", "some_attr"], # `True` will return all columns. One can also explicitly # list which columns will be returned. returning=True ) # Results can be accessed as a tuple print(results[0].int_field) ``` #### Ignore updates to unchanged rows ```python pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], ["int_field"], ignore_unchanged=True ) ``` !!! warning Triggers and auto-generated fields not in the update won't be applied. Unchanged rows also won't be returned if using `returning=True`. ## Using `pgbulk.copy` Using `pgbulk.copy` issues a `COPY ... FROM STDIN` statement to insert rows, which can be substantially faster than bulk `INSERT` statement or Django's `bulk_create`. Unlike `bulk_create`, `pgbulk.copy` cannot return inserted results. !!! note `pgbulk.copy` is not only available when using psycopg2. #### Inserting Rows ```python import pgbulk pgbulk.copy( models.TestModel, [ models.TestModel(int_field=5, float_field=1), models.TestModel(int_field=6, float_field=2), models.TestModel(int_field=7, float_field=3), ], ) ``` #### Inserting Specific Columns Specify columns or use `exclude` to configure which columns are copied: ```python pgbulk.copy( models.TestModel, [ models.TestModel(int_field=5, float_field=1), models.TestModel(int_field=6, float_field=2), models.TestModel(int_field=7, float_field=3), ], ["int_field"] # Only copy the int_field ) ``` ```python pgbulk.copy( ..., exclude=["generated_field"] # Exclude only this field ) ``` !!! note Columns that are excluded from the copy must be generated, nullable, or have database defaults. django-pgbulk-3.2.2/docs/index.md000066400000000000000000000043041473561042600166340ustar00rootroot00000000000000# django-pgbulk `django-pgbulk` provides several optimized bulk operations for Postgres: 1. [pgbulk.update][] - For updating a list of models in bulk. Although Django provides a `bulk_update` in 2.2, it performs individual updates for every row in some circumstances and does not perform a native bulk update. 2. [pgbulk.upsert][] - For doing a bulk update or insert. This function uses Postgres's `UPDATE ON CONFLICT` syntax to perform an atomic upsert operation. 3. [pgbulk.copy][] - For inserting values using `COPY FROM`. Can be significantly faster than a native `INSERT` or Django's `bulk_create`. !!! note Use [pgbulk.aupdate][], [pgbulk.aupsert][], and [pgbulk.acopy][] for async-compatible versions. ## Quick Start ### Examples #### Update or insert rows ```python import pgbulk pgbulk.upsert( MyModel, [ MyModel(int_field=1, some_attr="some_val1"), MyModel(int_field=2, some_attr="some_val2"), ], # These are the fields that identify the uniqueness constraint. ["int_field"], # These are the fields that will be updated if the row already # exists. If not provided, all fields will be updated ["some_attr"] ) ``` #### Bulk update rows ```python import pgbulk pgbulk.update( MyModel, [ MyModel(id=1, some_attr='some_val1'), MyModel(id=2, some_attr='some_val2') ], # These are the fields that will be updated. If not provided, # all fields will be updated ['some_attr'] ) ``` #### Copy rows into a table ```python import pgbulk pgbulk.copy( MyModel, # Insert these rows using COPY FROM [ MyModel(id=1, some_attr='some_val1'), MyModel(id=2, some_attr='some_val2') ], ) ``` ### Advanced Features Here are some advanced features at a glance: - [pgbulk.upsert][] can categorize which rows were inserted or updated. - [pgbulk.upsert][] and [pgbulk.update][] can ignore updating unchanged fields. - [pgbulk.upsert][] and [pgbulk.update][] can use expressions in updates. ## Compatibility `django-pgbulk` is compatible with Python 3.9 - 3.13, Django 4.2 - 5.1, Psycopg 2 - 3, and Postgres 13 - 17. ## Next Steps View the [user guide section](guide.md), which has more examples and advanced usage. django-pgbulk-3.2.2/docs/installation.md000066400000000000000000000002371473561042600202270ustar00rootroot00000000000000# Installation Install `django-pgbulk` with: pip3 install django-pgbulk After this, add `pgbulk` to the `INSTALLED_APPS` setting of your Django project.django-pgbulk-3.2.2/docs/overrides/000077500000000000000000000000001473561042600172045ustar00rootroot00000000000000django-pgbulk-3.2.2/docs/overrides/partials/000077500000000000000000000000001473561042600210235ustar00rootroot00000000000000django-pgbulk-3.2.2/docs/overrides/partials/copyright.html000066400000000000000000000005401473561042600237200ustar00rootroot00000000000000 django-pgbulk-3.2.2/docs/reference.md000066400000000000000000000000271473561042600174610ustar00rootroot00000000000000# Reference ::: pgbulkdjango-pgbulk-3.2.2/docs/release_notes.md000066400000000000000000000000511473561042600203500ustar00rootroot00000000000000# Release Notes --8<-- "CHANGELOG.md:2" django-pgbulk-3.2.2/docs/requirements.txt000066400000000000000000000217131473561042600204720ustar00rootroot00000000000000arrow==1.3.0 ; python_full_version >= "3.9.0" and python_version < "4" asgiref==3.8.1 ; python_full_version >= "3.9.0" and python_version < "4" babel==2.14.0 ; python_full_version >= "3.9.0" and python_version < "4" binaryornot==0.4.4 ; python_full_version >= "3.9.0" and python_version < "4" black==24.10.0 ; python_version >= "3.9" and python_version < "4" build==1.2.2.post1 ; python_full_version >= "3.9.0" and python_version < "4.0" cachecontrol[filecache]==0.14.0 ; python_full_version >= "3.9.0" and python_version < "4.0" cachetools==5.5.0 ; python_full_version >= "3.9.0" and python_version < "4" certifi==2024.2.2 ; python_full_version >= "3.9.0" and python_version < "4" cffi==1.17.1 ; python_full_version >= "3.9.0" and python_version < "4.0" and (sys_platform == "darwin" or sys_platform == "linux") and (sys_platform == "darwin" or platform_python_implementation != "PyPy") chardet==5.2.0 ; python_full_version >= "3.9.0" and python_version < "4" charset-normalizer==3.3.2 ; python_full_version >= "3.9.0" and python_version < "4" cleo==2.1.0 ; python_full_version >= "3.9.0" and python_version < "4.0" click==8.1.7 ; python_version >= "3.9" and python_version < "4" colorama==0.4.6 ; python_version >= "3.9" and python_version < "4" cookiecutter==1.7.3 ; python_full_version >= "3.9.0" and python_version < "4" coverage[toml]==7.5.0 ; python_full_version >= "3.9.0" and python_version < "4" crashtest==0.4.1 ; python_full_version >= "3.9.0" and python_version < "4.0" cryptography==43.0.3 ; python_full_version >= "3.9.0" and python_version < "4.0" and sys_platform == "linux" distlib==0.3.8 ; python_full_version >= "3.9.0" and python_version < "4" dj-database-url==2.3.0 ; python_full_version >= "3.9.0" and python_version < "4" django-dynamic-fixture==4.0.1 ; python_full_version >= "3.9.0" and python_version < "4" django-hashids==0.7.0 ; python_full_version >= "3.9.0" and python_version < "4" django-timezone-field==4.0 ; python_full_version >= "3.9.0" and python_version < "4" django-types==0.19.1 ; python_full_version >= "3.9.0" and python_version < "4.0" django==4.2.11 ; python_full_version >= "3.9.0" and python_version < "4" dulwich==0.21.7 ; python_full_version >= "3.9.0" and python_version < "4.0" exceptiongroup==1.2.1 ; python_full_version >= "3.9.0" and python_version < "3.11" fastjsonschema==2.20.0 ; python_full_version >= "3.9.0" and python_version < "4.0" filelock==3.16.1 ; python_full_version >= "3.9.0" and python_version < "4" footing==0.1.6 ; python_full_version >= "3.9.0" and python_version < "4" freezegun==1.5.1 ; python_full_version >= "3.9.0" and python_version < "4" ghp-import==2.1.0 ; python_version >= "3.9" and python_version < "4" griffe==1.2.0 ; python_version >= "3.9" and python_version < "4" hashids==1.3.1 ; python_full_version >= "3.9.0" and python_version < "4" idna==3.7 ; python_full_version >= "3.9.0" and python_version < "4" importlib-metadata==7.1.0 ; python_version >= "3.9" and python_version < "3.12" iniconfig==2.0.0 ; python_full_version >= "3.9.0" and python_version < "4" installer==0.7.0 ; python_full_version >= "3.9.0" and python_version < "4.0" jaraco-classes==3.4.0 ; python_full_version >= "3.9.0" and python_version < "4.0" jeepney==0.8.0 ; python_full_version >= "3.9.0" and python_version < "4.0" and sys_platform == "linux" jinja2-time==0.2.0 ; python_full_version >= "3.9.0" and python_version < "4" jinja2==3.1.3 ; python_version >= "3.9" and python_version < "4" keyring==24.3.1 ; python_full_version >= "3.9.0" and python_version < "4.0" markdown==3.6 ; python_version >= "3.9" and python_version < "4" markupsafe==2.1.5 ; python_version >= "3.9" and python_version < "4" mergedeep==1.3.4 ; python_version >= "3.9" and python_version < "4" mkdocs-autorefs==1.2.0 ; python_version >= "3.9" and python_version < "4" mkdocs-get-deps==0.2.0 ; python_version >= "3.9" and python_version < "4" mkdocs-material-extensions==1.3.1 ; python_full_version >= "3.9.0" and python_version < "4" mkdocs-material==9.5.42 ; python_full_version >= "3.9.0" and python_version < "4" mkdocs==1.6.1 ; python_version >= "3.9" and python_version < "4" mkdocstrings-python==1.12.2 ; python_version >= "3.9" and python_version < "4" mkdocstrings==0.26.2 ; python_version >= "3.9" and python_version < "4" more-itertools==10.5.0 ; python_full_version >= "3.9.0" and python_version < "4.0" msgpack==1.1.0 ; python_full_version >= "3.9.0" and python_version < "4.0" mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "4" nodeenv==1.8.0 ; python_full_version >= "3.9.0" and python_version < "4" packaging==24.1 ; python_version >= "3.9" and python_version < "4" paginate==0.5.6 ; python_full_version >= "3.9.0" and python_version < "4" pathspec==0.12.1 ; python_version >= "3.9" and python_version < "4" pexpect==4.9.0 ; python_full_version >= "3.9.0" and python_version < "4.0" pkginfo==1.11.2 ; python_full_version >= "3.9.0" and python_version < "4.0" platformdirs==4.3.6 ; python_version >= "3.9" and python_version < "4" pluggy==1.5.0 ; python_full_version >= "3.9.0" and python_version < "4" poetry-core==1.9.1 ; python_full_version >= "3.9.0" and python_version < "4.0" poetry-plugin-export==1.8.0 ; python_full_version >= "3.9.0" and python_version < "4.0" poetry==1.8.4 ; python_full_version >= "3.9.0" and python_version < "4.0" poyo==0.5.0 ; python_full_version >= "3.9.0" and python_version < "4" psycopg2-binary==2.9.10 ; python_full_version >= "3.9.0" and python_version < "4" ptyprocess==0.7.0 ; python_full_version >= "3.9.0" and python_version < "4.0" pycparser==2.22 ; python_full_version >= "3.9.0" and python_version < "4.0" and (sys_platform == "darwin" or sys_platform == "linux") and (sys_platform == "darwin" or platform_python_implementation != "PyPy") pygments==2.17.2 ; python_full_version >= "3.9.0" and python_version < "4" pymdown-extensions==10.8 ; python_version >= "3.9" and python_version < "4" pyproject-api==1.8.0 ; python_full_version >= "3.9.0" and python_version < "4" pyproject-hooks==1.2.0 ; python_full_version >= "3.9.0" and python_version < "4.0" pyright==1.1.386 ; python_full_version >= "3.9.0" and python_version < "4" pytest-cov==5.0.0 ; python_full_version >= "3.9.0" and python_version < "4" pytest-django==4.9.0 ; python_full_version >= "3.9.0" and python_version < "4" pytest-dotenv==0.5.2 ; python_full_version >= "3.9.0" and python_version < "4" pytest==8.3.3 ; python_full_version >= "3.9.0" and python_version < "4" python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4" python-dotenv==1.0.1 ; python_full_version >= "3.9.0" and python_version < "4" python-gitlab==4.4.0 ; python_full_version >= "3.9.0" and python_version < "4" python-slugify==8.0.4 ; python_full_version >= "3.9.0" and python_version < "4" pytz==2024.1 ; python_full_version >= "3.9.0" and python_version < "4" pywin32-ctypes==0.2.3 ; python_full_version >= "3.9.0" and python_version < "4.0" and sys_platform == "win32" pyyaml-env-tag==0.1 ; python_version >= "3.9" and python_version < "4" pyyaml==5.3.1 ; python_version >= "3.9" and python_version < "4" rapidfuzz==3.10.1 ; python_version >= "3.9" and python_version < "4.0" regex==2024.4.16 ; python_full_version >= "3.9.0" and python_version < "4" requests-file==2.0.0 ; python_full_version >= "3.9.0" and python_version < "4" requests-toolbelt==1.0.0 ; python_full_version >= "3.9.0" and python_version < "4" requests==2.31.0 ; python_full_version >= "3.9.0" and python_version < "4" ruff==0.7.1 ; python_full_version >= "3.9.0" and python_version < "4" secretstorage==3.3.3 ; python_full_version >= "3.9.0" and python_version < "4.0" and sys_platform == "linux" setuptools==69.5.1 ; python_full_version >= "3.9.0" and python_version < "4" shellingham==1.5.4 ; python_full_version >= "3.9.0" and python_version < "4.0" six==1.16.0 ; python_version >= "3.9" and python_version < "4" sqlparse==0.5.0 ; python_full_version >= "3.9.0" and python_version < "4" text-unidecode==1.3 ; python_full_version >= "3.9.0" and python_version < "4" tldextract==5.1.2 ; python_full_version >= "3.9.0" and python_version < "4" tomli==2.0.1 ; python_version >= "3.9" and python_full_version <= "3.11.0a6" tomlkit==0.13.2 ; python_full_version >= "3.9.0" and python_version < "4.0" tox==4.23.2 ; python_full_version >= "3.9.0" and python_version < "4" trove-classifiers==2024.10.21.16 ; python_full_version >= "3.9.0" and python_version < "4.0" types-psycopg2==2.9.21.20241019 ; python_full_version >= "3.9.0" and python_version < "4.0" types-python-dateutil==2.9.0.20240316 ; python_full_version >= "3.9.0" and python_version < "4" typing-extensions==4.12.2 ; python_version >= "3.9" and python_version < "4" tzdata==2024.1 ; python_full_version >= "3.9.0" and python_version < "4" and sys_platform == "win32" urllib3==2.2.1 ; python_full_version >= "3.9.0" and python_version < "4" virtualenv==20.27.1 ; python_full_version >= "3.9.0" and python_version < "4" watchdog==4.0.0 ; python_version >= "3.9" and python_version < "4" xattr==1.1.0 ; python_full_version >= "3.9.0" and python_version < "4.0" and sys_platform == "darwin" zipp==3.18.1 ; python_version >= "3.9" and python_version < "3.12" django-pgbulk-3.2.2/docs/static/000077500000000000000000000000001473561042600164715ustar00rootroot00000000000000django-pgbulk-3.2.2/docs/static/dark_logo.png000066400000000000000000000230301473561042600211360ustar00rootroot00000000000000‰PNG  IHDR¹Ì/B.÷zTXtRaw profile type exifxÚ}Qٱà ü§Š” ”C°3ó:xåg9¶óyÐ ¯®°ÿÿ½ÂcˆT 1åbÕŒ ±Æ* —BKžS3Å©T=Æw<ôºÜ*ŽïÀåCJÕ“ðñþ :.ÜpKg EÇŸ_¸'–òMä({æÍ[p"•…³ûÝ+µZò­µÍè.å Screenshot `—<îbKGDÿÿÿ ½§“ pHYs%%IR$ðtIMEè .òæwætEXtCommentScreenshotû« ƒIDATxÚíiPTY–ÇÿùXJºéT¨–tLeѶÚVKT,«Ú¥•ÅPv\z¢Ãé舙¡*&bzº¿Lõ&bº?LOwEk)›ˆ›JYZTYn…¸UÍÄ$‹PÕâÔ`‘ô@’ÌA2ß}˽ïÿ—*{ó½—¿àÉá»%žÈæpbV³Ó°ÓfÐ4Öûkƒ[ïÉꈲQVØ ¸8%K>NCѪ¸&c;—5­Sþ~ÐÑ5a7t¼FwFµv™ŒäÂäϬw[ÁjU@ èÆŒOr8hâJtÓC®¥ï­4jCos81«¦UèÝSÓB®gäD- ©f_d«î5; èצ´Þû–¡'ÐO—×wFZh‘àjRÖ:º÷ÁÕÔ…ÙÕh‘àŒ´rÍdÉwOÜq¼ T¿_%Ý-º© W+zÂMá-Íü]¥>àZ|8•”)*ƒ|¢æp ¸Ö¬jyñaPG”CÑä“óx[ÁjÝüï™ä7÷;®?Ôìõ”òÙ{ý„ñÑ ¹Zño%õ½ÇýšB>öÀf'¬ßþŸlØEYŒJ¸¾z¡~SK÷ÅÑÅì´%ÙÇ·¹–€'‡ 8W¾zá-Â.p‘²ŸUÞ뺣 ö0–gc$Ð%\?ÙNî¶Ë½ d»/mIöç‰f9®·?>£û"ôÑ¢n®>¸Fˆ®ˆ8ï;‡®¦.YaFC‹ÂMøDÍÎE%«o°ªvxžЖd‡£¾/ÞÂÎQåe‹¶Ñ‚‰AGzÆrAïO\ /[ÿBç®|}8NKÜì4]?B9.!/.™°–\«Ü‰ñ¥k£@Â|¬j-ÂêQþO[’ rùVEíBãɾ³Á~!JQØ›‡‰n>Qstÿ¶óp5šãá&°§Y[D¹¿âÁ7 ò¡‚hÕ,8Áí¦Ûâ肽ºÍmCÓe…]gk.‰¸š=¾½ ¨Õ±'þ¹ÛÿzoqoÉÕ ŽV¸åV×mÑÛšKf|Ôz“ä»-¢Xso³n„>"¢Zóä“«8ùÞúZs½2%3nöcET³æœ»,ÜA® à…·pN¬¹é!Wz«ÞæpjÚׄ4³_®‡Ë ä>ÉáŠÆÁǃ8W.‹i-¹Ò[õä×eÑÃ/—Œ8ùß|»,¦ƒœ'—ÅÐ+Ù€GZ/>%=7s;a#úånkÒ¹HjK·m}¥·9Ä:–o÷®DG¯‚Í6ÞÞÞ8yª§OWt›Ãé–ÏÝa…—Ñ!WÒM)ŠrôÈ¿cÓ¦·&ü,fݼý÷¿Àö¤4tv>¦¯#¸+J»)¢è—ÿøö €*8xÞýç_‰¿øt³Y’Öa;h‰ä‡¿újÈ{éßÄÅnÆÒ%Q´ør%­¸H~xfFš[wèÐA±Éå¤y©®›ÑM‘$ Y™énýmB|,‚‚ɉr¥›¢… ã㶸 ®··²2ÓÌAž†aD‰>çê*;+Ý£¿ÏÊÜIómñ$VÞa5äJùâ¢Yñ°°¬_¿Î£1AAHHˆ%ë`VK.Ú®fnN†Ìq{‰LÑ WÊŠ‹$___¤§í”5víšÕ_dh𴌕«9ï'ƒ©¥)‰ð÷÷—=>?/“L°ÙÜñ\6—c×Φ ICÈÍèª,_þ–-[Ê4‡Ÿß,¤îN&By‡\±­[NwÒ¦“§aÃé´/?›åÞ’+ð©Ùßß)ÉÛ™+44o¼±Ž(5ºO.š«²'}'|}}ôí3ˆRž!íØA%”“­,”[·l„-x‘jdK.RTeýúµ Qö ’$dgïâþyDIBV¦º~óÊË™7˜È’ó(A6âb7kRÑ“—kœp¢–ížUƒ\ˤx½•¥Mô#9ifsûx}ÏÉ]aTX˜v»’¯¼òŠìô]-äIú¬»í+¸†\î¥zH­°átÊÏφÅbáî9ð2&KÎ ___ìIߥ­/<7¾ÉßÃà4²¢*伟#£„R’·ë’ómˆò8 ƒ dÉ™œéº¼îßÚ@ù,¹úúÁ–`ùò×dÿæ›ÿ‘=Öb±`ß>¾rÍ=MÆÓ2}šoÈ9öóXS`÷8Ä4>co*-:9¯qWìÜ‘({|cã|ñÅâxù)¦kØ“ÎI8ÑCc¤uN’jkÕZéi;˜ #JÊ*‡1]ÇÁùdŒtµä ¬žyµçæÈoÑÓÓƒÚÚ:@ss+îßÿBö\áá‹°jåt¿O§kp¿ðäÍßc-Œ¨8ycÿ..9ÁöÓ9ŸEN_­kvUƒ\©ÑãpÓ—‰5O¥¨øø„WVÕ¢··Wö|Û·%Àj ÆÕÓcÿD2Ú ©©  @ÄÇm‘=þ³†F´·5ág¨¨8#{ΑvÏéº=OC‡z¬Õ¸wW:¢¬Ü¸,¬…¥¥SþühQÓuååfêÒîY”€ª>¥NéåÁea-Œp:»Q{ûòË?ãÆMù§Ø©Yz§¤ô)LbÇ“‡( kaÄÉSgñôéд¿g^€jœÏ"ÇŠëU³«*䃎.Åür½¿Yœ¥e/ý}]ÝE8ݲç‰Y‹ÐÐÍž‡HE1ª[r¥\–¶$»n¾9kaÄõë7_XpNÖÓ§C(-c³æû5ÊgñI—õíªWÕ!W2&ª—oÎZQRvÒí…éðð°ì×IOÛ?¿Y\ZS=Û‹hâ“+å²èia-Œp:»QWwÑ­¿}ØñõõWd¿ÖH»çu­xÔáÚÿi¹R.‹Ö<9iSaDyù©—.8'«¨¤œézg:ùY¯ç¯g»?M WrÚeÕtšÃЇpxxø…ΙT_;É~ÍÐЬ[Í•/®w'4ÍBˆJZó¶$»& /[¶”©0âêÕ;<<ŒÒRþòYXܽ›¶j¹’Ö\+ÐYcÏ3… _6Îg²<9Zm7…‡~–šn)iÍÇ@Wi!êïï)ò #œÎnÔ}pIõÅê”oª$)Ú*C®›€‹.hšB®´5€¶‚Õª€ž–ÆvbDiÙ ¸\.ÙãYw@s²÷*Òî™ÅM±W·qqŽæÛú^… ŠÏÙV°Zq×%—56Îé› xÐÖ.{¼Õ€„øXEž­l£ÆÉ ºä®Ø o)º‚>úú˜µX¸0Töøú¯ ãÑ3_«5g=ðv¨@~”†§Þòº@>èèRå!(:k?•ÒÒ“ŠÜOEÅ™ UDžêõ×WÊ>¾|¨ š)1ާcptËB¬jQ¥¨¢-ÉŽ¡‚hÙ°">~«ì×ïì|ŒK—ë¹—ÞÞ^TVÕ2Íq`_Žæ€óvBˆ®©¶JG[FÕe•mÕ33RÙ #Ê*˜œJ»,»v%{´c«à¼f¦+䃎.UüóñVýëÃqnÃ.I²³ä/8].Š·å'ëþý/ÐÜ,ߌäÞÌÜŸÅ'j3༹)\@®¦>•¯>ì±[71m¢Ô×_Agçcůÿð‘cLãss2^ÚîÙ'9m«™çõ 3.*ƒ«Z4}&ØY #JdîpΤʪsLý¡¡!xsÃúéW «G7…+ȵýe°³Ftv>fJ“}™úúúqêtÓ“ÏuOŒ8xót1ƒU-°C›ãÊÇ^#É{u²_ÛÁt‚CqÉqEœ“uäýbäåÊ{oÚôlÁóÐ9{ý‰‹ñµ‚u³¼Ÿší5; è×<]«©  ÎHíj‘VÔX¿Á|‹wº`•Õ³d$—Ë…ŸÿâmS²'ú‡wþ?ûÙ)¡ *Ø". ä<‚>Y6‡sbŸ¿¦oUa¾-ׯ]½î64v·â3çÕ¡žp5­B.ä<¹/rà¦hvéNAAäÜ |ôê&fs8UI“&ȵêFïqpCB>jÕ9—`'÷d滨>èè]º„Íd½ p/ÂZrraÈz›r‚|oÓ@N°Ü“Å]îŠRr5uavõZ$ø CÑ| £è={©¿ðBÞø]ç4vÔQj¹M®ئ„{¼ZZ`óÖDþ鈴䕙>eaÄ ë¢1.þ$øËâõeK݆z²ÂÃ!:zn“%Y¾¾¾¸wç*SCýÔ´Üùßg%z‘sÿÁ³Í’Ñ–š Æ/rÇ7 }öaðH“·&oÌLµ ùþÑRüæÝ_ɾ¿„øXjRüA«¤ìl¶Jü¢b÷OMž¥F;‡gÎVáŸ~ùŽìƒ±FŽ/Oÿþö÷†{ï%3±+~ôCÙãPq²’ë{©è?˸fÙ£H»g‚\HƒŒ~€ãU[[‡žžîïóXÑq¦ñAAˆ‹ÛJ‹&ìÚ™Ä4‡ZMƒ”Vss+nß¾Ë4Gž ç ä*+-5…éĈöö¯ÐØxG˜ûemº&úuÙíž r”—›Å4þý£ÅBÝoí¹:8ÝLs°6ï'È5T̺5 aZpž>S#Ô=,’Ï0ͱkg S¸• ×P¬'FTVÕ ±àœ¬£GK™ZdøùÍBZj Aλ”ˆ”–Vyï;áêÕŒ.KAλ2öîfŠù65µàî½Ï…½ÖÃBCÙºüäjß”$!'›-VT\.ô3¸øáGÌ[ô¹99¯Ú²y#Ó‰Ožôáô™J¡ŸËåBI)[8që–°Ï#ÈyTN[žJUõ9<<ŒcÇJ™æX¹b9–-[Js±àÌf³8gÎVbÁ9Y'*ÎàéÓ!¦9DÎN4 䬅{>6¯r:»QS{žiŽ”äíÂæ³òýùÙLãïÝÿMMÆm•Æš‚ëëë‹ÌŒT‚\/ùûû#%…­oHiéIYwÐÜÜÊ4G~~6SäŠ gPêîdÙ¼ÐÓÓƒ³•50º<)ÆžJ¶àyظñM‚\åç±¹*gÎÖ```ÀðŸÓ{÷ìf*'È=²âlÍpzzzPSsfS c8q¤Ýóv‚\õ —$æ¤þ“'ÏšbÁ9YÕ5çÑÛÛË4ÇÁù¹Úb-Œ€cÅÇaF  üÄi¦9ÂÃaÕª¹ª®J6Û‚³¡á6ÚÛ¿‚YU¤À\”p¢Ï·ãÍ ë™æ0Ja„\}ùåŸqí[Eÿöm °Zr5”——É´½ÜÓÓܰd3¶Üi÷œN+­‘D¡4¦9ÊOœfN=5‚êê>d®èÏËÍ„$I¹’b-ŒÌ³Ã9“\.Ž—³%¦".v3A®è‚3‹­züÆÍS/8'ëXÑq¦Š~ BA±+W,g[p–VÙãÔÙù—/Ì4GLÌZ„††äJˆµ0ÂéìÆù Ù/¸oìüç³¹…´àœZò)sEZêN¦œ~‚ì…ÃÃÃ(**#¢§y6ÅŒ›C~~³º;… gkaĵk7™­•‘u¼üó·ÜÁy¹\­]³š½0¢Œœ3­Wίcš#441ëÖär”ÃØtÒéìÆÅ‹—ˆä™ A ûþݶ¸‡<0p.¶oO`š£ìx-8ÝÐg hm}À4ÇObñýï¿J{´àTà욊»­?>ÆXžóÀ·p<î7¸Œ V\µ´RÐBš4êV(zô‰ eâÝ¥/‚¬Å–Ù -žÓÙ?…"§nUþÒkÛJ4‰ñú%äc‘ÇF£ÞC¨…“)º? KÓúù„íÀ5Ô ]×þù®æÞžmœ ™Õàq¸A³]ôšY&ÖðÌ ùçŽ?ÛÂòr~«#áÃìÍb˜Šþ›/„iCCPICC profilexœ}‘=HÃ@Å_[¥"•‚vPqÈPu±‹Š8Ö*¡B¨Zu0¹ô š4$).Ž‚kÁÁŪƒ‹³®®‚ øâìà¤è"%þ/)´ˆñà¸ïî=îÞþF…©fWP5ËH'B6·*_Bý¸ÄL}NSð_÷ðñõ.Ƴ¼Ïý9ú”¼ÉŸ@gºaoÏlZ:ç}â+I ñ9ñ„A$~äºìòç¢Ã~ž12éyâ±Pì`¹ƒYÉP‰§‰£ŠªQ¾?ë²Ây‹³Z©±Ö=ù Cyme™ë4GÄ"– B€ŒʨÀBŒViÚOxø‡¿H.™\e0r,  ’ãÿƒßÝš…©I7)”º_lûcîͺmÛvó<WZÛ_m³Ÿ¤×ÛZôo×mMÞ.w€Á']2$G Ðô Àû}S¸z×ÜÞZû8}2ÔUê88ÆŠ”½îñîžÎÞþ=Óêï®r¾0ŠŒÒ ziTXtXML:com.adobe.xmp ý?NbKGDÿÿÿ ½§“ pHYs  šœtIMEè "%XD+a IDATxÚìw@TWúþïô ½JÄ‚ˆ½wMÖÞcIÖô˜ºÙÍîf³©›o6¦hb"öhb‹¢ *‚ E@©Ò{ïÃ0½üþÈþ²nb âÌœ;wžÏ_‰À9ï}ßsÏsO{Ëh4Rl6\¦ ›Åh4öi”2•\¦îëÕ(djy—J.×*z´}ÝjE—¶¯W§nÕöuéÕ麾_ü­„bã;P%`±Ýyv<‘ _âȳ“òERžÈŽ'¶ˆ…R{¡½@"æ‹Ø,è WB[À`4v+zZûºÚ]òöyk™¼åj_cµAgÚ]Yì™"÷P‰gÔc«§ÄÅEìè,vàq𠘕VÝÔÛ^/k©èn(êi8Û]^nÐÒÍȵvƒ¢ƒ†8xû9xzIÝER@x`ôC‹¼½º«©´«6¥­$V^gu0o?Ç9$Â%p°³¯¯ƒ‡'@X€ßìôzZ*ºêsÚʾkÍËÑ)™ôtÛœ†Npââäì#ˆn€êèë.í¨Én-=Ü”“¥SØÂ#¿ì2Õ3<Ì=ÈÏÑ‹Ëæ  °! CmwÓí–²¸ÆÜ½²j›õÃHŽp£×è‰^áÃÜ‚0,Àdt}Ug}zCalý$ ù ÅzÕ}äLïÑaAŽ"{8@s¾÷+;ëSò÷Ô¦þz>ør¾Ð/j´W(Ƭ˜&Y[Z]Þ¡Ú´UíðÆáÊbÿÙ+z–ߘaî<`(4ÊÜÆ’“Õ©Ÿw•ÂÉx®Ý3þSføGrp‡7Зڮ¦Äê¬j“ixPËÚyÍyØŠà)^Cù Z½6»¡øXŵ/ºËá ³2‰'Ù8sF@¤³#¼ €$=ªÞäêœÏ+±«Ç’H(Ö¾“ žìëä o°4Ͳ¶ó7þVsµÝh€7HñW·ˆ5C¦‡º!A)€KPÙYª,ùÏpMØjðÔ°^!梢£îPñ¥wÛnöBy{mlqü¿Ú à «‘^!,È€‡¤¶«iÑÅwZoÁVÄËN!O…/ìêW@-½‡Šâ1×o½üËkÜcÃçzÚ»Áú‹L%?]rm[å%9…eõì œ³|ØLd‚ð;hôÚÄòŒ¿–œ-Ð«á Æ0žk÷~øòÉþ£9l6¼à—Æ[M%ÿÊûᤲÞ`$Ï8¿4ri°‹/\à¿4ÊZ¿Îûñƒ¶<¸‚ñ|å?cmØ\ÌA TZõ¥)[KÏaºßv˜Î·ÿpäš±>apØ.·ï¼}û’õÛ&ïyŽ}jä£Nb¸l‹.¥lïísibæÏ1\‘#[ `±Ýyv?ÿcƒ¶×`¤(Šº¢éFø‰pŽ`gÄšÉþ£qj lƒÑ˜RóJþñÛz•Õ?˜Í›!ñ*ñò¶sqH¤±„'ó…bžPÌŠxÂ~æÊ×ô*F­U«t¥N¥Ôª{5Š.eo«²«YÙ]«hÏèk¶Fÿ Œw="Ÿ³ÔA(ÅÛL¦½¯ëóœï­%£Ã&‰ïX§À`ow;G‘£ƒH*ˆ-ö­Ú§Vv(ºÛûº›úÚëäm%²Æ“=• F=#Æžô“1F Å;Ìüð¿Vuó…üãw :Û¹Eê7ßkT˜[P ³·€Ë§•m:ƒ¾MÞÙ(k«‘5vÕÄu–féLj$»ü¦?±PÄà}æÐ¥}žsâ=«Úå9’#\à<ÌÑ7ÐÁ+ÈÙÇCâB·yj£ÑØ*ï¬êj(ÚzçH_šÊZ»Aÿ·ÉÇ÷Ì@#Ȫ/Ü–{ÈÚ¿U ]þà9j ð¡nþ6‡†ÊT}Õ] ymåçšn}¯h±^W{³8F®Ÿ‰w¬…Fsûü‹õ)Lz¨ç-S¦D:Óxÿb«¼³¤½:³åNLs.ÍçÜ~‹O¼'mýLA€URÕÙðFV¬UŠÞ Åú4pæ£C¦ºÐûntµNSÖ^“ÑTSŸž®ë³.'o”ú|½ÅSꊷ ¬ƒÁP~cMá Æîue±¿²ø‘)t[.þ5zƒ¾²³>«±xOíõm¯µxx$G¸?ê#¼†àµ‚+ WÝ÷ÙÍãoÛÒý-«Å^oG®³–ËOt}IkuR]î§7ª :«°ùÔ°å‹C¦à°Кʎú—3÷WuØà³Ÿ¾báÐÉVÔI)5ª¼–²S•©wÓßÚ¼Ç?7f9ýGZ`‹ÆÄŠŒ•ùÇl9§Û'Þ“ž‰\Êëßa`úÐØÓzµ&ûóšk9:%í|ÞqðÛ¶8%xÝ €F¨têÝ9g^iH…+þîñÆøÇ¬ñCU­Óä6Þ9T~õëžJÚ¹Dä¾sâ“^¸chB›¼óï±{eÕpÅÏð× ùÖ6øy$WÖ^ûCÙµ7[²éiá®è@ôS¡îhi@˜¢–Ê'2÷0,!ÁÃ<}Ä«~„fYÛÅÊô·ª¯Ò0û+‹}&òñhßhi@ŒÄòŒùùGà‡{Ò:÷mgzè]JY\Yê_+/ÑP.Ü0#( - ,V¯‹½}îéš«pÅoqqĺك£™ñ,=*yBEú›åË ZZvjزGB§¡±Ñ6\` ÈÕŠ·Òö¡÷¿?I·ó,BÉʰٙóÞ>4x¡+‹F¯ùÒâ“Gò.â»#`!Z寥íeFâI³+å# ¹LKeÓÞ×}¼8q[ò;í š»1b!މA€y©è¨ûão¬(‘YîLyÕZŽ?(uÝÍ1…Þ¡ÍyohÀ“Ém(ž”ò zÿþSÝÝÄÔGóuôüǤ-¹ãŸ_-ö¢ƒ=OT&È‹Ã(˜…ËQ™_· pÅ|&Ë[˜ý€#¼†ÄÌ~õìð•ƒÙä=@ ÀôÆ—æåa»çS!kfü3ò9¼…!“Óæ¼ù±÷:hÀ‘ü‹hx`ôÞœÓkËÎÁ ¿¯ÙFžÔYìðÒ¸59ãŸ[$t!kɦЋgŠ“Ðöˆ€E`F¡ÑkwdøKS\1`tK?·©çUjT'Š/?^™@ÖŒøˆu³‚£Ñü0“ß¿q½ÿC¢ÖilêyE|áÆ‘‹J¦¾ºÖnA3æåI¯ÍCóƒ€Ð«î{#uÏûmx…À@vñûvæËßÎ&hÃ’œ˜âÖJÄŒ.¥ì¹k_~Ñ]W<<\6×6\ÀåoõÈí /ÌIˆÔn4lÊØÝØÓŠFý¥MÞù|ò.ô5 á‡mÓ/E˜çàïgýùC/2Óñ9:åsißt+qrúAKoÇ“)_~§h‚+LÂd‰7œ ˆ_^{qÄ:"I„~Tµ¿y#F¥U#p?e­ë“?ûQÕW˜ŠaREQ‹Åš=8:}êkËDî–¯ý«žŠ¯rNaƒ"ü&Õ«®}–¤‘Á&$ÄÉNø™g¯üËkœå«þSãó¥)üF#]×W˜–Á΀ÿÁŽ/z-zÝÉaË,_õ’¢ò›Ê¸GS¦e¶ÀÑÏÑ~ø,ëÑÐi7£Ÿ eó-\õæ¬o[z;@ïovþè?•ÍÆqoF M˜ñ§bKVz[¯úGæ[;šèý 0= N¸ƒìÝ¿þÒkÎÃ,Yé^YõÁ¼88€Þ½¿Ù8ËÕúo„77øíIÿ{EÓˆ>]s5£.·Ø.²ÖMi_¢÷7c¸¢e¡3à‡þ àò·E­:¼À’•>‘s ¹Û!¶ÚûoLÙ™ª•Ãfbç¨õB)üÐO8löºˆùqák-Vãƒæƒ¬#:ƒ·ØmòΧ®…ýþæc_ÐÜhßðÃ2wÈø«£7Y¬º]=?ÞI†Û!6D—RöÔõ]Ôp…™Øá3uýˆðÃÀ˜0æFÔV‹U·²äLU'r^Alƒ^ußk×wŸU¶Áæûö*r)[?‚(ŸðÜñÏ[,kÐß³ªtH`:Jê­ûcåup…9Ã¥ŽýãÆ‘‹Ðû?<#¼†\Š~Ö2ð¢é‡¢+ð¹©À•tD«×¾ãÀ{¸ÝÅ<|í?sÕðÙöB;¸Â„ä7•ÍÉØÕn4X ®²i¯:#m€‰ †ÙÇ_­¿W˜–ÁlÞßgÎ ŒöºÀæ «¾`BÖ Tô„}À—3^°Ù«{ LæHÞÅàS1[à¸Ôcäx¯°·!O‡˜•ŒºüI7¿µ@Eg‡¯\2‡0ŠKåé òÂÊx®_ê³sä‰\RO‘³»ØÑMìäãàá$¶‡˜×†½Yœ›³ÿî&q†Ã! !»¾(:ë›}üpŽ`¢W€›«ÀÞžo'å‹$<±€Ëç²ÙB®€Íb ¹|›Íãp¹l.‡Íá°Ø<—Åbñ9<4Zq,?~C¹Ùø|àõúø ð6€ Tu6LOÞÞ`ÔÛÈónµéèï/õ$qu—8; ¥"¾Í€Ư²ØVgö]ò'¾8Ì#0XE¡}ÝOÝØÍìÞ W´Ú=b„s`“··ƒ‡Óñ þ®d±ž³¤QÑùaG¡Y+úwþ»Ý^á²9ð9FÖŠR«~)y×^Y5#ŸîyÇÁ³¼"ÂÜ‚ýœ¼ð¢Ú½ê¾­IŸ¯h1k- ëgƒ·!V‰Þ`ø(ýЛ-Ù {®—BûGòŠ$k¶L£¬uaÒÇz3žÝÄ“\œ÷ (!VÉ…—W—žeÌãl’ø®ðŸ0vÐ0lÏ?‘ßT6:ý ³VqrزGC§ÁÕ+#·¡8*ók<ˆ7‹ó†ïäÙþcƒ]|Y," îæLqÒò;§ÌW~(›Ÿ6ïŸ8Ú °&šdm ’þmÖѱ˜Î·iðœ)þc„ÄܽÁðNZÌûæLmr xÁºˆùp5À:èQÉ_HÞu¤ÏŠsÛ®{<2¼ï¶áƒßC¦’¯¹òq‚ºË|cмùïà+`4ÊZ·¥í9­lµRûW‰=_{tô Pìêý§¬½fXÊ'æ+ÿèÐGV†Í†Ÿ!´¦¸¥rSæn+½Ýw‰Èý•a‹Çù†£ëàûÂËk̶å!œ#H÷¶@?÷$C·(ÉUÙ#Ò>·ÆÞ?Š+Ž _{tÎëýG¢÷ã¡Óž´0SázõÕê,8#:b0N%®/;ou–K(Ö®àù™†o+ðð4ö´N¸ú¡™½OáIã¼…ÜPÐ µNóÙÍבּ÷Ómdñ¬¿­‹˜Þ˜„Aî»ÃV˜©ðmoNC1œ  Jêíû_oH·.³œ3£ž~kòã^ön"0!³‚£Ÿv0W·ÃåIðpÿÁyéQÉÿ’ºgµåù‰ ž¿,tÒs3QÝÙ0øÚGf*¼dêkÁ.¾p2F„i“w>Ÿü¥uõþ[¤~eÓ^_±½?0ÎÞ±Áæ:·_™ c@˜†žÖ'R¿LTw[‹ÁŠºdáÐI8Ø,€R«^zés¼ lnÞ‚÷Ä|¬Za@ˆŠŽÚ¥ÉŸXQï¿Eê—?ý/6½?° "žàaKÍQrµA—ÛTCÈPÒV½èú+Úìhð¢/glóuòDì€%‰ö0Ó±€3Õép/€ùMeÓR?/7h­ÂÚEB—â)¯¬1[§åá°ÙÛF,1GÉŸt·ôvÀÃK÷þs2vµ Vaíÿ Š><óµ!®þ E¨{໑æ(9§éÜ @ïØÜ+£6¾2n­D FàYÖ›cŽbO×a/½ÿ¯Xk7(iÚŸ¦Fâæ@üœ¼Þ÷Œ2y±{eÕͽíp/½ÿùÄ{Òî/ù8b½Ðé£$t–9ŠÅ^ €Ù)m«¶ŠÞ_B±"Öo· ×gÞreòbrá[€©è¨}4í ú÷þsNÙS_™<!ôdùà©&/sgwY—BßBÌB}wóÚ´]ôßñùšó°#3^ vñCÈm uÚ ñ6y±Em•ð-Àô´É;7§~IÿÓ^ßÌzoÊER„ к'b±žbú Sóá[€‰éVÊž»þU’†î£ËK#7<1úQ.›‹úåÊæ›¶Ìo[oiôZø`2”Õ_Óöž¤÷•î“x’¢É/ÏŠB¼€µ ä ^ö›bÚ2« ºªÎø`tÝG™GvÓ;Ãó‰÷÷Ó_ê€xëbš¯é÷å·VÀ±`4÷äœy·í6ü«[Ä—S_p“8#^Àêvñ]!ö0m™ ·áX€ ø±$ù…ºd:[ø•ÿŒLÜ„û{•Âb±ÖùO2m™ûä5]Jl…<YõËŠOÒÙÂÓ×o³K¾Àªå1ÔäeVu`ð0 ¨³aÅÍ}´5ϕžùÄ⩈°v|= ]L[fAN@J·Rö\úž£žžæáŠ’&nïHÀb±V{›øÈú¥–804zí;Ô]ô4o‘ÐåÔÔ—CÝ)ÀF›zèh_#– aÿ­s;ºJéiÛZ»Aû¦nóvð@˜“pdò2kºáXÀƒ‘\•ýlm=m{ÁqÈ®iÏ»Ø9"L€aˆøÂL<(í¬ƒc!@ugãªÛ‡èiÛ?ÜG}0y«T`‡0F2Ñ-Ô´¦·—«€þÒ§Q¾žCÏ<Ïÿò÷× !­?`0CMœ¼v_W™Z§c!¿Ñhü*ç=³ýìð™úò¸5Øì˜—½›i ”SÆúž8ðû\©ÌüKS={ÿ§"—rÙÄ0±Ã®‰´×t7Á±€ß¡¾»e}þ1Úöþ6B˜‹ÅZdêuà;X†ÜµNóVÖANýèÞØaNþ¦-0µ³ ^…Üï /ÅÊi÷™ðO÷Ñ/[…ÞØ^&~»M}j% ¸7%mÕW&Ðͪ‡¼6n-V}­ámêu`Š¢d­p,à(µê7³ÓͪµvƒÞ™¸EˆŸÀöp6Ã!dž^à^/J¤Û¾Ïñ\»íÿˆÓ^À6ásxkíLœ¢² ¿¢¤­ú šMþH(Ö¾ñOºK\`³Œ°÷6m·ºªáÕ»ÁÌ2M'~½—úšNרÔÚÔÜÒÙÙÓÑÙ£PþgIÇå:9Ú»¹:{{{xòâñð^Ђ ©eÒOö²ªO :¬¨AþËé;It›üÙ8kJÀ„Æ„TU×]¼”û}r}×ïìñwo];{á¼iîî®ðY™zÜn4´Ë»<Ͱ¼l¥ØúPCOëcåq´2éE§¡F,DÓ4*µú›½G§¬úûû{â·÷§(ª¦]ñ÷g'/{=ár ¼Gw;g“—Ù"ï„c!EQ£qÇ-z]ó;oÿ÷qxŒÌLƒN§ûðã=ï~sñAÿP¡Ñ?þÆ·çâ.Çq5ÃF &y;  (ŠÊ¬ËßÞYL+“>Üì$v@»4‰WR¿=“5à?úŸ*«jáFRØ $ŠeÚ2«{‘@Q òïù'heÒá!‹Â<ƒÑ(M…V«ýh×÷YÈÁ£?“ĺ'6{¾ØÝ´eõÔñêä«I]ú¼ãàeÃf¢EšÒ²ªÒƇ ñžÓ™]p&)BíL|ãi\oÁ`€cmZšem›+âécO›ûÆØu˜ú7-y%&)çv~1œI o±‰7Ut=*9kÓp¨è­ìù&|•‡g¾LLâµl“”s#3Î$…‡ØÉäev*»áXÛ€ÊÎzZÝ÷ò†køŒ qh‹¦¥­­#>«Ê$EÅžÍT*Up)\„¦ßÑ®ècmTŒFãž‚óô±'€Í}qÌ 6‹…¶hZ2²n™ª(…FŸ_p.%‚£Pjò2[ûpÀV  ¹üßEô±g×ð®vNhˆ¦E¯×ï9tÁ„?u ^%#"Ó @=ŽØ¦è †4ÚÕ÷´CÐÌ`Lþ˜Aæ K²ËÛLXà±Ä‚šÚ8ÖòˆyB“—Y)ÇQ›€Û%ûä5ô±g‰ßxÜðnΜO2y™‰WSáXË#2Ãe¹}H m{`4÷”\¤•IÏž(kÇASÓÒÚ¾û”éù·ï—Ëûà^ #ä \Y&42^ ßÚ–µTì‘UÓʤrƒvý]Å­•hˆ&äZJ¦9Š•©t&\Xýg‚Àô)ázUÐr€%‰4´*G§œ‘¶3µ=‹iÐh4»bÍ•Þ5æHœ^C¤–ÆW`ú 8 f[PÖ^C·¼o?Ón4LËù:û¤J«F‹|Hro–7÷š©ð¤ÛõwJÊád ã.°7y™]JkCp´ä Í-|¾öÚãW>)nÁtÐCqâ”yÇyç.&ÃÉÆY 1y™Ýê^8ÖV IÖöN«̱W4Hû|gÖ‰ö>UµuÇ ÌZÅÎc)mmpµeÀôGº0d;p¥ú¦YûrýõÈKïÍÇ(õ}í†jI¾ž W[×ô;A»ÕÛ¥Výïê«ÖesƒQÿXyܰø·bnmèiE3í ¥rGŒ%2¼~§Ñhàp‹!äòM^f³)¾mC²ê ôV¹¸Ún4l­ºìåý¿\û:³®@‰%âûúf^k¯%\TÚ(˹U‡[ Ëô‡%›ÕÈGQÅðôF£ñ`E’µ?ÅÇÅwfó¶yöêh'¡íþ"ЇŽ[î”ßñS—Æ ·[sdƒÈW``mPÞQÓK—£¶_øMûº!}ÀÑrƒv[] U—BQÔ_Ý"¦y…‡¸xÚ»"™EQ•52,·êøåÂm5uþ¾ð¼%FlÓOT¤jåzƒÁ%ChĵÚ\šX²Lä¾uô’•¡3?Ë9ñ¯ö‡Ý©òA[ÞmyE…²ù«]‡Gº pä!qv;ØfféøË–NÔsérÚÖÇW£w¶ÏŪuj1ßÖGÒ,£ÑÈÔgSë4Ñq§ÉÀ¥‘fEQe0¯Wç¼Yx2Ukú}ƒÙ¼GÚ¹yˆÜÄŽÞR÷§Al¦æÈd½£æ½ Ñ[´%Û ¹ç?—J%03…Í#oì0y±­sßv¶sÄ€Éí†&½ÿHŽp¼oÄOÿÍf±¦FÆ ~º$iS…‰§­Ë ÚO:‹©»ÎÐ[¿$ùÓ&YS£¬×b_ Rõ¾ÃH d ¾9ŠÅ¶:& @¯ºïƒ&ºœÿšxï[_<¤.OG.k˜ýݳ¼YæZ˽­WíÉ?ÇÔ@—”§‘Iï~½ ±©,ÐI™gYK¡Å=ÏÌ€ÜÆ;rŠËïzDºÜwªÑCêòøèG ¼{vøÊEBsØðNë-¹ZÁÈ@Ÿ'šœçìù«°NÔzœæc®\m¸MKD÷çפ»…!“OÎû[þÄ?ñždòK0™ÿ¶½£s籂ì:‘ÖÚŠ f­N'0Så§myt°d O:Ü#¸ÿ¿Ïas†ym·ªrá7¢žüÐ+ÚTJ`ŽÓ4ĹžJ~–/)©¬,SLÝt§­š&ó?OLåqâd1_ååö¼vyyGmNK鹦['•<¾¸Eêç$¶gX”µZÝW±ç‰›±+6î‹g | @ t ³™.w¿Lñõ%y‚pÏ!ážC6Ž\ÔÞ×UÑY§³6«£üëž8øṵ́…Ì‹r^Aqaù¼Ùåͽ9· &DAob]àZ`f €Î mÌ¢Åç¿CÐ wèjçäjçí;bE}¬S7ÊÚjº›je-Å=õWdÕ9:å=ÿêÔ°ec¼‡1/Ð'Ï\¦‰%ǾO€X*,3Rª;²t´Øñ²Èg¬ù rAÎ>AÎ>?ý¯Ñh”©ûº²n•¬S)ëTõöjûø’(¯á¾N <ÖØÔG—<?\+~©º.0©¬ 5FŒ€¢ö*šX2Ú+Äbu±X,¡ÄA(¡¨A¶Ðp“’3heOÂåëO=±Š¡1`weµ•ÒÁŒÇ%þžRW´0³ ÞUêûâheÒö˜K2®™5 X­…ô{X§Óìé(¢ƒ%øŽEó2Ù¹ùõ]JZ™¤ÐèÓ2rs`¦„•JÖ'µÝÍíFZ¤gãŠæe&Ž~Ÿ@C«b_ÐëõˆŽé|æ` ˆPÞYG3æ œÙ»¡y™ƒªêºÓ)%44,µ¨©¨¸ 29z2îAúGf+-º†ež£Y6y1‹H¼’F[ÛÎ 5Àv@¿vÐâ¶îÑîCжÌ\Þ÷éþký>d IDATÚš÷õé-H dzÀd= ´÷uUÓc^o°+¶„›…Œ¬[2­§n¯^KG˜L ÷CúE£¬•fhXæàBBŠUØyäD¼™¶®Û&r ?ÄwÒb Ÿ£–ÉéêîÙ~Ð:öØœN)©ª®CÈLE»ZfŽb¹l6|Ëô¨zSµä/½šÄ“¸ˆаLNjÚM+²6ár*Bf*ZÔ=æ(ÖŽ+„o™#í}Ýt0ã×0œ09z½~Ï¡ VdðG{ã‘ÈTTª»áÀïÐÖ×E3ÂýѪLNAaIvy›¬ÑSo 5 0 ‰æ)O÷2GZú:é`†·Ô­Êäœ9Ÿdu6{è3•,àðà^æ@м‘¸ Ë%>\6­Ê„h4š]±qVjüîSÍ-­âÃЭ2×Z:FÌ…FyÇ@>cÔ(G¬›˜ÜÛ…åÍV¼æêµ ñáÀ\{»…Æ€ÌlãÄÂO‚;L̉S‰VmÿûãT*d8*™™Jñ Œ•œf8!œI©­k<–hÝyujÚ7sòÊÓÐ×a®‡÷2Dºé"öhR&äʵ xŠÃÇ‘hà”Ê͵¹CÀƒ` È¤Ø íФL…B©ÜÏ€ù1­¬¢²F£ñª¼ÁL…c˜A ¡…H“‘u3¯µ—!‡€â‘h@È5Šrƒ¹îƒó‘ €]?ý¿È˜ÇÙs©§G†°>(ÝJ3n#sÖh Þ,˜ŠŠÊš •ŒyÞ˜z#a}PÌ—à+œ#àqð¶2E:Ôä·Š‡ð°Èd0oÎdÏ¡8N‡È>rse!t{™#]Zq|ùÓ “õn¹Ä°‡Ê*m-(Dj £®×\à#t„{™#J½–¸ Ž˜R4i9=÷Mžúñ2‚û@É̵ÈS„œ] €-ù) {.Àèõ†˜Ãùh{ÏÞlljAˆû‰Á`¸Ðk®í³ž"díeôèÉ'ÂÁB“P\RžZÔÄÔ§KJFj þÒ®è®6˜kÕÄEˆƒ K§@,™Áù‹É ~ºûâ”J¤êͽíæ+ÜY[˜$€!};¥0øë»”7sòè~ùJfÆLÚN" < ôâzêMÆ?ãÁï."5P(“Õ›¯p{`RŒÞê‡B«Õ}{žñ—^QV^pÿ.©æ+\ŠCûÓÒMtÖK^Aqa]·-- U@†\Þ÷é~bó?.œ& ¶=¾”M=ª›7‘èÃhÙq…gƆ8 ‚“™&ö|1Ìhîƒ<Y·d*2[§Â|ÃÃB)Šš6eA<ŽÔ@ÿCY{­YË÷–âóFBZŒJåhRýÇ`0ì?Jìî—§6.äñ¸EyòÜ´p4)3.dT–•W¡1üLn«y/ÎôÂ!æ €“Оfë©ÄQ€Ð˲ª«·êHÕ>yBäÏÿ½ôÑ™ýŸŒÆðF£ñ‡æÛf­ÂCŠëà'Žô´ mrÌõûã7Xêÿ­Kƹ»ÿ÷K0"|Ø0bWD}|àjW¶PE5ÊZ¯h̘ðIû7÷1Nì…tÉîÝ kC«ê]Ý=Û^%Uû£ §Ýý¿|>ïé z#%- M‚¢¨â¶j³–å '3Pìø¢6—–ÔÊp´§_¤’ˆ62ÐeDxè/þqÊı½ñÍ8­“‡Ô•&óÎÿ vô“(,k±},Éï¬F«ú]ôzýžCÄ–·n˜ÏåþòsÁÝÝõ™H™t»ª#¿ ØÆ[E¯ºï£öB³Vák@QTˆ”Û{÷µhõZ4¬ûSPX’]Nl®lÒ„{ì/^0 O~°™ë~‹âV³ï†ò²Ç † €¿Ôƒf4õµÝ˜úΜO"Uõ3+&¸¹:ßóGáÇF&¶I<6.·¾¡É–[Åæ³–ÿ¼ã`¬3V¼$tÙÝUÚQ‹†uZZÛwŸ"–ýñü©¿õ#‡³už¹|õ†Í¶ µN³§1Û¬ULt ÅÛÇX ÏöÞ¬¶R4¬ûp-%“TÕ#]†½_q×áËóyÌE…ÂFSÝi­ºc0o6õg?¼}Œw‰‹7‹CKŽ´j° ðh4š]±Äî~¹çòïÝ8;9¾²a:)óZ{Õ™7oÛfÃHj0û… ~N^x+\6g¹#-6ù–´em5h[÷$÷vay3±¤Ù“'üþ^Ïs'ôÏc ƒ­µ •V½³Ñ¼'!–‰ÜDöx+Er  ‰%™ÍEh[÷äÄ)b×`=¿z²ëo,ÿþÏDÁÐà©#ˆíO¸Ymƒ© Z*ªÍœCeŽ{Þ>† }Nyì¯ÏÐôh^¿ ¶®ñXb©ÚöïÓžÍfo^7Ÿ —ââSl­a\®Ë1wn8Ìtt¢K¦ïT­¼²³Íë\¹Fl—Kä`·û/ÿÞÍø¨Q|‹”©Û^íè´¡ŒRÝJÙߚ͞ cˆ V€™.R×(®˜&Æd5bèP(•;bâIÕþÄúùN÷ØÛKÿôø\‚¾ºžzÓvFf½Ùß” og±ÞA† ›ÅZæNcvÕ$c/Ðÿ(âͼÖ^5©Ú'°ýsfN$ÙxöŸÓjm¢ñÆ#Õfφ=Ïs$^@æ EQ£\é2Ó—®ëËo*C ûù=?tü"©ÚŸ_=ÙÅÅéþ$8ÈAt)ƒ ëºoçÛDj ª®†Cr³O–Žp Â;hê@c~¬¾ö•52*IÕ¾ðÁwv²X¬õ+çôØ÷§m"5ÐåjKLv;ûâ´ ðvpŸÄ£ËÝïµåµÉ;ÑÈ(ŠŠ¿œJªêèþ/ÿÞMÔØg;)³]¼UWÏðF{Õ}ïÕš}þç¯n"¾ï M›Í^í1†>ö¤ÔÞB#“Éz·Ç\"Uû–u°ü{7vbñË[HÄðdZíí£ÙwKÏwÐV€¢¨HÏ¡ô1擊D•Nmã,-#G£7’ª}âøÌœ>ž ßvÄÄ÷õ)˜Ú*ôý7W,PQ¸;NØ’ £Ó‚Oº®/½6ß–[˜^oˆ9Lìî—×MuvrðŸûûù¬˜>œ”ñ­½êŒ,ÆŽ šËÏ*Í~'Ä©Ÿ›Ä™¶#öB»¿¸ÐèØ÷¥ñ:>\\RžZD,Íý‚9›Øgõ²9½{”±©Ž”YâFèÅÞ‘°) (j¦÷(úsZÙšYW`³-ìüÅdRUG‡x ü…Œæã$"õ—skKJ+™×**:j·wZbŸëH¡°5á1˜Vöì¸s^«·Å+¿Û;:w#–ÙfÀË¿w# ^|bI¿Æ¼†q¼$ɵ<*rósÄ%À¶'nçWœ‡ÑÇžï-×krm°y‘Mið0Ë¿w3urÁ§øìprG£R5ô´¾Ù’mŠÖùN`³Ù°5 (j‘_­ìùWñ9¥Ö¶¶iµº¯bÏ“ªý• Ófù÷n¼ynZ8š 'SR³˜Ô0ΖYhPíж*£½B%‹>ö\Ñt'”ÛÖÁ༂âºnRµÏŸcÊ{]–>:“ '¿ÜN£aHj FYë u–XÚ ñöqð@o£`/´û‹'½6l+9ÛÞgCi~Ož!–Ì`Òp¯¡¦Üý>l˜±t’Åõ=·ò ™Ñ*N”\±LE+ý&°X, ئPµ0 šVö4õ1ùq6Ò°›Zbãˆ-{l^;ŸÃ1eÛæóyOo\H²ß<Å„Ô@µ]M¯Ö[h9hlY†{Ï8Ñʤ7š2m$EhRrÁÚ'D›~Ê~ÊıŸè襼Ú:«¿bèp±…2‚¼ìâ)uEÿnÓÀesž œN7«þqë˜R£bv«R©Ô;öë¼²aº££é§kÜÝ]ŸY1 W¯¦Yu«(k¯µÌ抢–NBçnë@QÔd¿Qt3éGUûñb†gúÍÎͯïR’ªÝ´Ë¿w³xÁ4‚^ý,&^nµ© ÃŽ¼Ó–©+”Í=(;€r±süÄ›vßOT&”´U3ØíG¿O UõÔ>¦]þ½›ðáC#»‘z´Î>mF¦µž&I¯Ëÿª§Â2u½æ?UÄ s‡PE-žHC«^¿y@®ff¢ÇªêºÓ)%¤jß´fži—ï†Ãálݰ€ ocŽÄYcj ¥VýNáI‹U7Ýo  ?äâ³Í‰vù@Ϋ:¾½}–‘O¼BlªšÏagÞI¿‰Hî-Nº]§¤ÜêšD\éõDµ…N„<.ñpöFÏø/‡Î¢¡U¯5ÜH®Êf˜«åò¾O÷›ÿyqÃL{³VáìäøÊ†é=|Ž\r½Ñ$k{®ìœå^ö!3ЭCþ‡¯-R?¶êö¡š.F]û—‘uK¦"–önÞlKL÷-˜;™ ‡wMio·š{F Fãç¹?´-4m5žkåƒô€_<$‹õdè|Ön4ü)#†1‹ƒaÿQbw¿Lá3tˆ%î ‡Xž™âúžÜÛVèÛÛ?æè,wäŸ)Nb{ @îÉhïaOÚÐÓ¶­U—/•§[µ{»º{¶¼JªöW7Í6÷òïÝØÛKÿôø\‚Þþîä%𷇵·ÿÜhÑt ƒ'Pð›Êb=ö(mÍ[4»¾ÈzÝ›šFòî—Ù³,}ÚcÎL’çKŽ_.¬©­§mcèèë~æÖaKÖø¦ÛÈçAèÐ!÷#Ì3øon´5oÑÍ=wZ«¬Ñ±z½~Ï!bË¿3Fù ¶ôØ.8ÈAtAŸ'\N¥gc0 ÛsŽè-zýцasЛC~Ÿ'G,¦­míFÚô¯ª;­ochAaIvy©Ú7®žgù›ÿX,Öú•óúüÓ˜„^y CByúGí]¢xÝ5,ØÅ½9à÷ñvðø6p6}{R½zUê«Ó€3ç“HUÍç°¢£È¤ü‹á.%–sF¦ÒÑ05Py{íâÂï,\é¦Ð¹€ô“åÃfFqÅ´5/G§\•º£¶«ÉZüÙÒÚ¾û±ìÿ¯nšmo/%RµXüÂ&’]ϾÃôz¥’©äÛ2÷Y¸Ò?¹ q @Wè/RÝ¿G¬¢³…9:åêëV3¸–Bò\’å—ïfæt’;O’ói”HoÐo¿ù]‚ÚÒ—žâóðÀLôõŠ3­oŒËÒ)§|ZÚVCsOj4š]±Äî~™éoùåß»ñ÷ó^1}8AÎÆ%Ѥ%œ¹síý¶< Wú–û¨P÷@ôã€|lëÅQK%­ïŒ¾cÐÌIýüvc ̽]XÞÜKªöÇVXþý«—“ÜòåñÔÖ¶âÍàf}᪒3–¯wKØ @€·ƒGlèšÙ`ÔGfìJª¼I[ OœJ$Uµ˜Ï7v$qŒæïJrI)™tj êÎÆ5Ùû,_ï~Ó|=щCȢɴ=|7³o<^pÉ`4ÒͰںÆc‰¤jyÓl©TBÜ B¡àùÍ ðEÌy5¹Ô@]ŠžgÓwW,Ö›ÅY:=8`àpÙÜ7"×Ò|"è'Ö•û4ó¨J«¦•UW®Ý ©‹3èrôÚ”qk/oîÍÍ%#à ò/7öY~á—¢¨/p¶sDx(|= _f¦þ¹1ãÅä]í}]4±G¡T'Uûܱƒ‰.ÿÞÍ /M G4à»SR©uš÷2î•UÐ~㢡“)xx ™ô'—áVaê^Yõò«·TÒÁ˜¬›y­½ÄF$ëWÎe±h4t[ú(É[çN\)ªª¶h"n½ÁðEö÷>ñû3oX!æ‹ÐwAL‡Íy-rõHŽÐ*¬MÕÊG¤}~¦8IOôrp£ÑxèøERµÓdù÷n"ÂC‡ù84àÒË¥Ò ßdŸ²p²ÏŸù‹KØ8Ÿpt\“ábç¸gÌf+2xùSï¤Åô¨ˆí¿¬¨¬¹Al òÚãsé°ü{7|>ï™M‹ðñ¾Y¯Ü2Ú¿ïÖÙmõd®&–P¬gG-¡ÕàÀÆø ‹ šgE¿ß–·üòGyM¥Dj'šŠrÖôñ4ŒÈä‰$¯£RhôéfO d4äÅ=SCìâ‡ÝC ²wG0=kÂçnsjE'idcÒ¿Œ¹uV¥³è\¼LÖ»=†Ø…$ ¢ƒ‚ýiw7—gVܘ´÷Ðy³¦Ò ßæžy¢2Ô>*r{4d*z*€Yàq¸Z?…'µ.³·V]~âò§åíµ«1-#G£'v(aÝŠ¹´xdÁ4‚µ§5Ý)3Sá:ƒnWö¿ý)ŠúWä!O@€™p¶sÜ=~«Uœ ¸›ïM¡)ÛÜ>¯4ÿA½Þs˜ØÝ/öBnÍ–ï&løÐÈÁn øÑ<©Ô:ÍöŒc/×_'9¾ š‹¬Ÿ³3ÄÕÿdÄ:k´üñÊ„M—?Îo*3k-Å%å©EÄRU¿´yŽÄ޾©¼9ÎÖÇHž Þu"­¥µÝ´eöªûÞ¸¾çoÍYŸk™È}ÕðÙ€X€™ÁãöZå s'•­£Ó¿øwúáξn3Uqþb2Á¤çòïÝLIÖ€kɦÜÙ*ïx:i玮R²õþØ "Lþ@,Æúˆÿtm¥Æ¿Ñ”91ñ½ ¥©½Ö´%·wtî<–B깎¦çòïÝ899¼²a:AvîS©M3XÚV³2é“ï„ï&Š ž?ÄÕŸ‹Áes^Zó„5¤Š»'åí#…Ç7\ú(§¾Øhº,r×SIæ%]¿ržUl_0w ÁÚ«Zå¹¹&8 {¥"søõORµr²ÎÜj°2 “?‹#â ?œøÄ#BWë}„“ÊÖqY_¿‘üMUgÃצÕ꾊=OêYœíxc#GX…ÛC†MáCЀ#ß?TŽ&^ûmÎé¹y‡‰{r0›÷Ö¸Çøº#‰á¼Èþ«IOOâI¬ú)>î,rí£§n’µ=L9yÅ…uݤžbÛ¦¹vb±u¼Tlö–uó p*ùNeÕ··Ê;^Júâi¢Û=fï¨ žRW @Háiï¶ⳃÙVÿ òFS¦ïå÷¾Î>ÙÒ;À ¤Nž¹LÐþYÓ'X‘·££F‰ù‚ ,5PV}áÜ+ÿ·›D‚Ï_³Ãgê$ÿQè‚ „ tö>;ño‡Ïò|í5ïÄw M-±q¹¤Ì~dâ+ò³½½ôåM$g®ÿïÛx™ìRE)5ªÝÙ§&dí.ÐÓâΉ­ö^ŒÎ@ †ºùŸ‹~Æ•Åwý$»²~¨ïnéçŸ$%g4xÍò9V—ÿkÎ̉k×èié9ýüå²öÚMW¶?[›D×EqÅïDor±ï@Fx ¹ý,c4€¢¨mõÉW?x75¦¤­úþ;…T*õŽ}q¤ì´¢åß» ò_DЀ½‡ãôzýýG¥UÍ–²ý¤²•>®Ûõ¸›Ä}`vÞn½výÓmWw¦×æýÖ¹ìÜüú.%) _Ú2ÏZ–ï†ÅbmXEr)øFqsaÑýp6—o¸üñcåq´òÛÙá+Ã=‡ ·@,ÇW=“³÷λðö™â¤.…ì?=ú}AÛfN›`¥^9Â]JrãÌo¤êVÊvfycçi:}øSõ¥ß´C'¡ŸÐZ.žû‚~MжwùSnño~œ~¤°¹\oÐSUU]w:¥„”IK¦„X×òïÝØ‰Å/lšKЀo~HoiùŸ½¿½öBiêä„wÉfv»'¯»†=1úQ\ö ;ažÁ?Nza —±·’þ¥)cäËâßÿñNòéKWZ²zÙ«öäLÒ»W¯þÿÕ{£Ñ˜ÛxgsâÇ¿cÐÐÍQkíýuÜ.›‹îÅ’°L˜'ÀÖ¨ëjÞ’öe’FÆø'å5ëEa¡†­°hkq— ®Ÿû\,²n¡}éõÿû>©ˆTíþ®âË'?«émúºàÜÎî2zºh®ÀéÀ´—\íÑ«XÎ?ÿùOxa`8ˆ$ ªo.-$/ÅÜ$lu¯oœP;ˆC±YÜKo‘È“ ÇÑ8ûÛ‰½øÄb›h[=©rQùS5 ™ªNzúg WtxÊ 8ñ‹€UÒ£’¿u#æ‹îrj4:£ B'¼£”kY3¶Ÿë'Þ·Þ€ŸQ©Ô³–½TÓ®°t½Cy}„ÚA´>ÀèÊb§L~yˆ«zŒ¬!—?Ãw´¤·û²¼ÑVž™ÍÒ¹rT¡áè4 Œ¢¼½vkúîm/\A)^³ž_¥Tëx z–îZÚ/.Ù¸~)“œq3'ÉÓ›xÜéÈV†ñ•|½£5íçve±ûz3éRô|yøÓ®¸â¿íLOñštüZ¿FÇkг´¿ÓêÒ~øÀÏ×›IÐh´‹Ö¼Z\ßóðEÄ,U(_ÎÓz[ß®ùŸæý1ó`2Z½îXA–Êx¸âÞ#ƒ=¯Aǯ×ótœž_Θ­˜>ü³þ̼ç>y&~ÛûGþ½oÏV‡ðT!<—²ÎÓ²ƒÙ¼Sã±ê ° 2ë ω¥á©KZÁî3ðõ¼f=¯YÏkÔ±F&-ÿÞMk[ǘE¯< w(7WÌÕñ´Ö}#EW|hâ³Á.¾hó[¡IÖö·ÌýzëáŠ~b/7þsȤP_wO©«£Pʤä0ïôõWßßø½—’Òzp4¾\?WãÏ5ò™ðø]wMzÚËÞ -`[¨´êƒùž¡Ç%«VÇ$žd¦cðPo_©»§ÄÅMâì ´â+šóò‹>ñá¯ÿÝ diq´^\­7GãÃ5 •í û€'>á$²G{†Ø(9õÅÛr¦ëúàŠ‡ŸI˜jï?Dêå+uó´sq;:‰ííøÖ‘/H¯×/{ìÏÙåmz¶Î£sãhÝ9ZŽÞ™±ißrõjÔ1_„¦ °iº²ÏrN¼ß–W˜œ1\Ñ$‰Ï`‰§—³»ØÉI(uJ¥B±„oÇa“ì[FcŸF)SÉ;=-} }íçJsNëÛöÿ[ÄÍ[7b‡ÍA…Ê`0\ªHßTxÂÆ‹Y’%"w¡£·ØÙ™/qH _$â E<ÇrŸËáð9¼]l0½V¥Ó¨uj•V£Ô©åjE¯FÑ¥’u©å­ªžJy[²¢±Ü µA·K(Ö™QM ŒD „€ÿ¡±§õß9Çi››×f `sƒ¸ž˜ÏúÏ« _òÓð¡O¯Qèÿ³›«S§ì3è®hºá±ßb®ÀiǸÇ#ÅܽAŸX‘ñdá÷ F=¼˜Äë®aŽZë ”Âp?Zz;>Í9ñqg1\˜Á¡Á‹V„ÍâbÒúƒÁ`¸VýZÁ÷·õ*xX/ÓùöŸEn ÷DŽx@zT½‡òã_¬O+€5ò–û¨m‘+0í§´­úÃÛ'plXëg #IDATlî×a«fc³Xð<:ƒ.±<óµâSÈ èÏ‹NCÿ¹×ùB€)éQõž(ºòt͸ÐogwØŠÙÁÑ8äf¡¾»ywÁùprÐŒ¿¹E<7j‰»Ä®€ó’×TúIÁéCò¸g OúáÈÕã|ÂY˜ñ‡Ë 72ëóÿ]ôãYe¼H±7hîòЮ€K£ÕkSªs?¼sI€…ù«[ÄS#{;xÀ@¥V}­êæ'e `V‹½þ4bÉH¯Ìù@]Pë4)Õ¹•ÄA€™˜Éw|3ìüF"¯ÐTRkní,ÿQÕoS1’#|7ä‘YÁã\>¼´FgÐç4ï-½´WV o€‡a<×îo!‹¦ŒÁ^`MÆâÖÊãeIïáÜxp¤ZêÀˆå³G‹xx¬•ÆžÖ„ªŒ÷j’ª :xü.l¹Á.K-º¥9·ãµ1£Âá°zäjź¼˜Š«ÇÍð¸'Ü6½]†ZX¤aé)Š¢6/óÞ[/Â-À FcI[Õ…êŒw2ä ~j”°D+ÎVóë~9FL?õ¡·<Œ¢G%Ïn(ú®: Ŷ §Ç º­ßÒ°û ÷ü…½´ä±uKá(`&õÝÍ×ëò^¾ׯEÜmæ%×…%ZQž†_£»ÿ8Ð]*¸~îs±[€l.\`Sø8zFwkÙŸqÂS…òÔÁ\#'< ¿F',ÖˆŠ´,u¿ô¾µW™u{úÔñp0“Ä+i,­QX¤iŒ|–z0OÂSr(Cà5êê÷ÙrÃþíÁï.N›Ä¶2:ÄM!—÷[¸M¦úÕQ6¥õàh=¹Z/ŽÎ“£uãPlx˪0RüZ°T+(Õrd†‡)éòá·B†Á£¦‘‘uë½?EQŠ×¤ç5é©\Š¢(#‡Ò¹s´ƒ¸ZOŽÖ‹£såPø"¤%l¥‘_­ThåZ¶Ò4sâ“!¦a06=ý«·êøã’ËÒyp´GëÆÖ¹qŒ|É}^“^P¥Tèx:sìïÍ»ø¹³³#<`¥eUèý)Šb錼¯á¿C½#[ëÎѹqtî;GçÄÆÁì~‹ž_£ã×êøuº~.ê˜ëiY.ž¯Cs¸bª¢8ÝN·*ÕþwˆàÆÖ¹qt. [ïÌÑ9±±Šð°Ãsµ‘ߨçÕëx :~£ÞÜþÝ|·`Þ ýÓÛ¦€l„®îžs·Y®>6¥sbë9:¶Î…£sföµÏ4*ˆû0‰'™ïîìïgïùî{ûÓ³ ß>ýõkcÇŒ@P0L 5í¦Eë3PÜ·Ã (£(Š óu¿£ÕkZ•R£RhÕj½F­Ó¨tµ^«ùéßuj¥^­Ôj{uJŠ¢”zµJÿŸé&™N©6üçûÚÈ2²Œÿó1îÈñX WÈcsíx.‹cÇЏ1W äò…\˜'ó„bžPÄŠx!OÈ6õéÙ¹³&’€1ñ+—-°‹ñA€µÒÒÚ¾ûTA–,ža¾Âyž‡ç ”2/pAþ ÇÇ¥W2 µW‘ykæô‰x‰˜ vê1Ÿk)™kè6|(¢0X,Öú•óÈÚp໋ƒ±€«D£ÑìŠ#hÀÖ ó¹\ 4HTd„»”ä%½‰Ù5¥eUX%¹· Ë›{ 0iÂXDaÀˆÅ¢6Í%kÃù‹×°JNœJ$Xû3+&¸¹:# ìÈðéák]XµuÇ °xþTDá!ñóõ^9s8YRR³°2®\»A°v,ÿšŠUK çeûjÿyF‹@@€Õ P*wÄÜEþä†Xþ5 £G†ºKPX×};¿€«!ëf^k¯š “'bù×4…‚g7Î'kÉS—°ŒFã¡ã ðüêÉ..N„©˜6eYŽ$äÕÖ5"`TTÖ\Ȩ$hÀ¹“2ÈËcÓÂÑdm¸’t€+ þr*ÁÚ#»aù×ä,ûÃ,²|²ïB_nt€z#“õn!9cûÄúù0-#ÂBÃ|IÞÓÛÙ§MÏÌE €Ö¤eähô$ÓèO‰(˜>Ÿ÷ÔÆ…dmØôRA}Ñë 1‡IÞý‚å_ó1eRY®Þª+)­@ €¦—”§54`Nÿš 7WçgWNÎ|îb24å<Ñ÷3:Äcxè`DÁ|Ï®ñù‘äöŽNhG{GçÎc) زË¿æ%løÐè²6¤\Gj  ×So’5`âø1ˆ‚Yáp8[Ö>¼+© €fhµº¯bÏ4àÅuSs3yáMVÅõ=·n"@#ò Š ëº °`NÿZGG‡W›AÖ†ãH ´âä™Ëkñ†å_‹i-éLÇ jj M-±q$Oibù×’ 4c”/Y¯¦"@ ’’3È@|bÚ¶Þ[6{ãêydmؾ7^.ïC, €0*•zǾ8‚¼²aº££aIÆ-æ“rÉTºŒ¬[&;7¿¾KIЀùXþµ8R©ä•Í„¯ŠŒ9§×#5åè÷ kŸ4Ü+dh0¢`yæÌœ@Ö€¤ÛõwJÊ FUuÝ锂l^;ŸÃAC"@P ÿâ „w^!5$ñJY&DFˆÀb±Ö®˜KÖ†ÇRÚÚ:  €\Þ÷é~’ó?Xþ%KÔØw©€¬ É×3 @FÖ-™JGЀs§ ‹DÛ6ÞúelœF£A, À¢ †ýGIÞý2u„OÈÐ ‚,3§'k@i£,çRA€…_¼²ª«·ê°iÍ<6Mˆ0~¾Þ«f…‘µ© ÀÒ\H ™úŸÏa7 Q +—Ì&,— «kê°]Ý=Û^%hÀ‹f:8Ø#t`ô¨°@w Y.]NC ÀB¤¦¾ûeÞ쉈M Ïn$|Ķûz{刘½^¿çáåß¡C°üK#¦O&k€L¥»‘™‹@@€Ù)(,É.o#h–醗§ûæE„ïãÜw© Àüœ9ŸD°v>‡…Ó¿4dé£3Ép½ ±© À¬´´¶ï>E2ûÿ«›fÛÛKºæKøNæ³ç¯"`F®¥>y?{–éÇ{jãB²6ì:‘ÖÚÚŽX@€YÐh4»bIÞý2c”ïà‚žL™E܆¤¤‚ó{»°¼¹— Wcù—¾¸¹:?»’ðølWlœZÔ@`NœJ$X;ŸÃŠŽÂé_Z³xþT²”7÷æÜ*@ ÀÄÔÖ5K$ùjaù—þ„ âAÖ†cD¯¨fråÚ ²ÌÁò/íáp8[Ö>üõâªj¤‚Ó¡P*wÄÄ4`îØ€!ƒú3yB$q._G ÀddÝÌkíU4`ýʹ,  ?ŽŽ¯>6ƒ¬ Ûc.Éd½ˆ˜£ÑxèøE‚ˆùœqcG"Ö‚¹“ X5ú´ŒL@EeÍ…ŒJ‚¼¼i¶T*A ¬…¡C‚fŒò%kCÌá z½±€€‡%þr*YfϘ€(XÓ‹Ífo\Mø®àÔ¢¦¢â2Ä ™¬w{ É+÷æŽ ŒÓ¿ÖÆøq£Å|YÎ 5<$i9½‘ XþµF¤RÉ+›çµáëÒ[Œ^oˆ9Lòî{!§­”93ÉOÜ]½–Ž@@À).)O-j"hÀK›çH$v„5è¿xÂ`²6|{A¥R#0Î_L&kÀ¬éã+…Åb­]1—¬ U­r¤‚€ÐÞѹóX ADú#ÖKÔØw©€¬ GNÄFÄŒë©7ɰn–­±H´m3áý §SJ ­V÷Uìy‚Ø ¹Q8ýký̤Á$^éƒ,`eäÖu4à¥Ís$vbÂÚñóõ^3;œ¬ íGj xNž¹LÖ€YÓqú—!,ÿÃ,²hôÆÔH ý£±©%6.— Lè‡@0ƒ1£ÂÝ §rúöÐy¤‚€~‘”œAÖ€5Ëç`ù—1üg7¾%&£¤¥ ¨±€€ßA¥RïØGÐg;ÞØÈ“˜>5š¸ gÎ!5üÙ¹ùõ]J‚lÛ4×NŒå_Fáåé¾e1á›ÂvŸÊhniE, à~%}§6–É’Gf·áêµ ~“ªêºÓ)% xdâ‚yD„ óu$kÃûãT*b÷&ñJY°üËTx<îÓ’µ¡¦]q3'±€€{ —÷}ºŸäü»T@0•É“¢ˆÛpø8RAÀ½ÈȺ%Séð¦¹b±`*n®ÎÏ­šDÖ†ÓÊ**k ø Ãþ£ÈÚ0c’?3œEó¦·!© à”–U]½E2iâ’)!Xþe‡¬ ;öÅ)•H °a®¥d’5à…ÍóE"!akH¥’W·Þø[ߥ¼™“‡X@lF³+6ެ Ó§ŽC l“Ù3Èçý>øÝE¤‚Ø(¹· Ë›{ °búp?_oÂ6 ôdâ²6Ä¥W”•W#[äÄ©D²`ù×–a±Xk—“?þŸx±€ØµuÇ àã$Šå_›fìØw©€¬ Ÿ¸ÜÝ݃X@l‹+×n5àùÍ „B,ÿÚ4b‘hÛ–ydmÐè×IŸ„‡‹¢P*wÄÄ“µaÚ”(̤Að=‡. 5À†Èº™×Ú«&hÀšÙá¾>ƒàçë½f6á™Àìò¶üÂÄ`ÆCÇ/’µaù°ü hÔ~8ˆ@@l‚ŠÊš • ðq†@€Ÿ3*<Ð]BÖ†ýçs› ó!~%Þó›…ü„@Àv#ùÔ@W®¥#†#“õn¹DÖ,ÿ‚_0}j4qvì» P* “IËÈÑèIž}_?o$–Á/ðòtß²8’¬ M=ª›7‘À\ôzCÌaÂw¿,{t~Í’GÈ7ŒƒÇ‘À\ŠKÊS‹šè.å_p"‡…ù:’µáBFeYyb`&ç/&“5àÙó>~ Ç}zãBâfÄý¿öîÿ'ê:àxÇÁ1Y' OáHLä{ÜËq´Æ(´Q²V£ý?¤k«µÕO¹òˬæŠo.ÜÂF[Bá9IL¸ÌVn¸Ap”ÂÀãŽþ'_>Çë>·çãø¼v/à9yËûÓv‰]€46>qüL§ì …&Žñ@¦ù/m.W€€óóeá Oöïʈ]o`xè¨È7* ÄÇèìêf  ¸ÝsŸ5ü ;Ç¿x¨=»Ìâ3|Ñhu»¹ˆþÁד‚$ôÿâ¡R¶nÎ}|­ì Ž¡ñÁ삎o¿ë †ã_,€V«­Þ·[|Œ³Òß/нÝ`í“¡ÐÄ»± yOˆÏÐ`íþk”]€@pñÒUÙ^*ÉZgˆaXˆˆˆðƒUòÇE¶+삨ÞÌÌ챯¬²3<[Êñ/Áò´I|†£uç§§¹ˆ¨\oßÀ°Kòë8Ñ ÏHOfX¸ÍI ;2²3ܹ;{­ÇÁ.€º5·´ËPSeÑé8þÅb~4½\)Üxæœ×ëe@­†n9[;…ßuÇñ/–`[NF˜N+;C{Ï-®"*öÓ….Ù”fsü‹%Ðë=XýŒøÖ¶NvATéÞ½©Oê…ÿãwüB¥Š‹òÄg8ÒdŸp±  >W»íÿÍHþE{r\xZÊ¥IØ¿7?I| ñK´ÍëõÖ7 ¿ûåÕ-:]»ÀÒh4šÊrù߬ÿÞív³ &ü9d³;eg0pù3–%';-F*;Ãuç¤c€«€ªœk>¼ªÞ›cXÍ"°a«V½uÀ">FK+WõpMþ{¤É&;CYi‹Àòíx*W|†ÓçíÎávAÔár—ð±UŠ1‚ã_("ÞûÂÎTñ1:.r5PÇsê´ðñïkU%ÿB)åeÅâ3«k›ššfÀßýöûÍÞ›ÿÈÎ`ÎÏaPJVfjr\¸ì wîÎöþ:À.€¿³÷ ÿ…÷_ß½†E@)¡¡ºwÞ|^|Œ+Ý€ø½¿o >=c㚊çv³(«Ð”[Q,üJÑ‘Ñ1Aü1V앪a:íÑjW¯Ö³(+$$øðÛ¯¤?)8C†õ,‚ø»üíY"ÏM4è[OJÜ´À¢¢"¿<~Hð=EfN¶€ß‹7Æžxwÿ ?´¶ÒÜR÷ÁÖä$>øÎ:CÌçŸþ°¶låý^MIzï5R’f~~žOÁGúìƒ_ÓÖüc¿ï¦Ó–š·ä=™º}[¦1ŽcåŒM\ëqüÒ=ØzÁ11åÛ‹zÊLIûÊ-ùyÙ†Ož¨‰Çã™›óøf{„òž/øû÷ݾûI¢Õó!€b8           @@@@@@°¢þz‡£„ eÔIEND®B`‚django-pgbulk-3.2.2/environment.yml000066400000000000000000000003441473561042600173420ustar00rootroot00000000000000name: django-pgbulk channels: - conda-forge dependencies: - python==3.13.0 - poetry==1.8.4 - pip==24.2 - postgresql==17.0 variables: DATABASE_URL: "postgres://postgres@localhost:5432/pgbulk_local" EXEC_WRAPPER: "" django-pgbulk-3.2.2/footing.yaml000066400000000000000000000004701473561042600166040ustar00rootroot00000000000000_extensions: - jinja2_time.TimeExtension _template: git@github.com:Opus10/python-library-template.git _version: b7d2321846eeeb120ea9455597ddcf9bd8b5e7a4 check_types_in_ci: 'True' is_django: 'True' module_name: pgbulk repo_name: django-pgbulk short_description: Native postgres bulk update and upsert operations. django-pgbulk-3.2.2/manage.py000066400000000000000000000003611473561042600160540ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-pgbulk-3.2.2/mkdocs.yml000066400000000000000000000044311473561042600162570ustar00rootroot00000000000000site_name: django-pgbulk docs_dir: docs repo_name: AmbitionEng/django-pgbulk repo_url: https://github.com/AmbitionEng/django-pgbulk plugins: - search - mkdocstrings: handlers: python: import: - https://docs.python.org/3/objects.inv - https://installer.readthedocs.io/en/stable/objects.inv - https://mkdocstrings.github.io/autorefs/objects.inv options: docstring_options: ignore_init_summary: true line_length: 80 heading_level: 2 merge_init_into_class: true separate_signature: true show_root_heading: true show_root_full_path: true show_root_members_full_path: true show_signature_annotations: true show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true markdown_extensions: # For admonitions - admonition - pymdownx.details - pymdownx.superfences - pymdownx.highlight: anchor_linenums: true line_spans: __span pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.superfences - tables - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format - toc: permalink: true theme: custom_dir: docs/overrides name: material logo: static/dark_logo.png favicon: static/light_logo.png features: - content.code.copy - navigation.footer - navigation.path - navigation.sections - navigation.tracking - search.suggest - search.highlight - toc.follow palette: - media: "(prefers-color-scheme: light)" scheme: default primary: custom toggle: icon: material/brightness-7 name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: slate primary: custom toggle: icon: material/brightness-4 name: Switch to light mode extra_css: - css/mkdocstrings.css - css/mkdocs-material.css nav: - Overview: index.md - Installation: installation.md - User Guide: guide.md - Reference: reference.md - Release Notes: release_notes.md - Contributing Guide: contributing.md django-pgbulk-3.2.2/pgbulk/000077500000000000000000000000001473561042600155365ustar00rootroot00000000000000django-pgbulk-3.2.2/pgbulk/__init__.py000066400000000000000000000011361473561042600176500ustar00rootroot00000000000000""" Bulk Postgres upsert and update functions: - Use [pgbulk.upsert][] to do an `INSERT ON CONFLICT` statement. - Use [pgbulk.update][] to do a bulk `UPDATE` statement. - Use [pgbulk.copy][] to do a `COPY FROM` statement. - Use [pgbulk.aupsert][], [pgbulk.aupdate][], or [pgbulk.acopy][] for async versions. """ from pgbulk.core import UpdateField, UpsertResult, acopy, aupdate, aupsert, copy, update, upsert from pgbulk.version import __version__ __all__ = [ "acopy", "update", "aupdate", "copy", "upsert", "aupsert", "UpsertResult", "UpdateField", "__version__", ] django-pgbulk-3.2.2/pgbulk/core.py000066400000000000000000001020521473561042600170400ustar00rootroot00000000000000import itertools import re from typing import ( TYPE_CHECKING, Any, Iterable, List, Literal, NamedTuple, Tuple, Type, TypeVar, Union, cast, overload, ) from asgiref.sync import sync_to_async from django import __version__ as DJANGO_VERSION from django.core.exceptions import ImproperlyConfigured from django.db import connections, models from django.db.models import expressions from django.db.models.sql.compiler import SQLCompiler from django.utils import timezone from django.utils.version import get_version_tuple from typing_extensions import Final, TypeAlias UpdateFieldsTypeDef: TypeAlias = Union[ List[str], List["UpdateField"], List[Union["UpdateField", str]], None ] _M = TypeVar("_M", bound=models.Model) QuerySet: TypeAlias = Union[Type[_M], models.QuerySet[_M]] AnyField: TypeAlias = "models.Field[Any, Any]" Expression: TypeAlias = "models.Expression | models.F" _PRECISION_SPECIFIER_RE: "Final" = re.compile(r"\(.*?\)") class _DB_DEFAULT: """Sentinel value for a database default.""" if TYPE_CHECKING: from django.db import DefaultConnectionProxy from django.db.backends.utils import CursorWrapper class Row(NamedTuple): """Represents a row returned by an upsert operation.""" status_: Literal["u", "c"] def __getattr__(self, item: str) -> Any: ... def _psycopg_version() -> Tuple[int, int, int]: try: import psycopg as Database # type: ignore except ImportError: import psycopg2 as Database except Exception as exc: # pragma: no cover raise ImproperlyConfigured("Error loading psycopg2 or psycopg module") from exc version_tuple = get_version_tuple(Database.__version__.split(" ", 1)[0]) # type: ignore if version_tuple[0] not in (2, 3): # pragma: no cover raise ImproperlyConfigured(f"Pysocpg version {version_tuple[0]} not supported") return version_tuple django_version = DJANGO_VERSION psycopg_version = _psycopg_version() psycopg_maj_version = psycopg_version[0] if psycopg_maj_version == 2: from psycopg2.extensions import quote_ident # type: ignore elif psycopg_maj_version == 3: import psycopg.adapt # type: ignore from psycopg.pq import Escaping # type: ignore class LiteralValue: # pragma: no cover def __init__(self, val: str) -> None: self.val = val class LiteralDumper(psycopg.adapt.Dumper): # pragma: no cover # type: ignore def dump(self, obj: Any) -> bytes: return obj.val.encode("utf-8") def quote(self, obj: Any) -> bytes: return self.dump(obj) else: raise AssertionError class UpdateField(str): """ For expressing an update field as an expression to an upsert operation. Example: results = pgbulk.upsert( MyModel, [ MyModel(some_int_field=0, some_key="a"), MyModel(some_int_field=0, some_key="b") ], ["some_key"], [ pgbulk.UpdateField( "some_int_field", expression=models.F('some_int_field') + 1 ) ], ) """ expression: Union[Expression, None] def __new__(cls, field: str, expression: Union[Expression, None] = None) -> "UpdateField": obj = super().__new__(cls, field) obj.expression = expression return obj class UpsertResult(List["Row"]): """ Returned by [pgbulk.upsert][] when the `returning` argument is provided. Wraps a list of named tuples where the names correspond to the underlying Django model attribute names. Also provides properties to access created and updated rows. """ @property def created(self) -> List["Row"]: """Return the created rows""" return [i for i in self if i.status_ == "c"] @property def updated(self) -> List["Row"]: """Return the updated rows""" return [i for i in self if i.status_ == "u"] def _quote(field: str, cursor: "CursorWrapper") -> str: """Quote identifiers.""" if psycopg_maj_version == 2: return quote_ident(field, cursor.connection) # type: ignore else: return ( Escaping(cursor.connection.pgconn) # type: ignore .escape_identifier(field.encode()) .decode() ) def _filter_fields( queryset: models.QuerySet[_M], fields: UpdateFieldsTypeDef, exclude: Union[List[str], None] = None, exclude_non_updatable: bool = True, ) -> List[Union[str, UpdateField]]: """ Filter fields. Always exclude non-concrete fields and auto fields. Optionally exclude auto_now_add and primary keys. """ exclude = exclude or [] model = queryset.model model_fields = _model_fields(model) all_fields = { **{field.attname: field for field in model_fields}, **{field.name: field for field in model_fields}, } if fields is None: fields = [field.attname for field in model_fields] to_update = [ attname for attname in fields if ( attname not in exclude and not isinstance(all_fields[attname], models.AutoField) and ( not exclude_non_updatable or ( not getattr(all_fields[attname], "auto_now_add", False) and not all_fields[attname].primary_key ) ) ) ] return to_update def _fill_auto_fields(queryset: models.QuerySet[_M], values: Iterable[_M]) -> Iterable[_M]: """ Given a list of models, fill in auto_now and auto_now_add fields for upserts. Since django manager utils passes Django's ORM, these values have to be automatically constructed """ model = queryset.model auto_field_names = [ f.attname for f in _model_fields(model) if getattr(f, "auto_now", False) or getattr(f, "auto_now_add", False) ] now = timezone.now() for value in values: for f in auto_field_names: setattr(value, f, now) return values def _prep_sql_args( queryset: models.QuerySet[_M], cursor: "CursorWrapper", sql_args: List[Any], ) -> List[Any]: if psycopg_maj_version == 3: cursor.adapters.register_dumper(LiteralValue, LiteralDumper) # type: ignore compiler = SQLCompiler( query=queryset.query, connection=cursor.connection, using=queryset.using, # type: ignore ) return [ LiteralValue(cursor.mogrify(*sql_arg.as_sql(compiler, cursor.connection)).decode("utf-8")) if hasattr(sql_arg, "as_sql") else sql_arg for sql_arg in sql_args ] def _value_is_db_default(value: Any) -> bool: """Check if the value is a database default, they were introduced in Django 5.0""" return django_version >= "5.0" and isinstance(value, expressions.DatabaseDefault) # type: ignore - missing stubs def _get_field_db_val( queryset: models.QuerySet[_M], field: AnyField, value: Any, connection: "DefaultConnectionProxy", *, copying: bool = False, ) -> "Any | type[_DB_DEFAULT]": if _value_is_db_default(value): if not copying: return _DB_DEFAULT else: raise ValueError( ( "DB defaults are not supported when copying, " "please provide an ORM default with `default=`" ) ) elif hasattr(value, "resolve_expression"): # pragma: no cover # Handle cases when the field is of type "Func" and other expressions. # This is useful for libraries like django-rdkit that can't easily be tested return value.resolve_expression(queryset.query, allow_joins=False, for_save=True) else: return field.get_db_prep_save(value, connection) def _sort_by_unique_fields( queryset: models.QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], ) -> List[_M]: """ Sort a list of models by their unique fields. Sorting models in an upsert greatly reduces the chances of deadlock when doing concurrent upserts """ model = queryset.model connection = connections[queryset.db] unique_db_fields = [field for field in _model_fields(model) if field.attname in unique_fields] def sort_key(model_obj: _M) -> Tuple[Any, ...]: return tuple( _get_field_db_val(queryset, field, getattr(model_obj, field.attname), connection) for field in unique_db_fields ) return sorted(model_objs, key=sort_key) def _get_values_for_row( queryset: models.QuerySet[_M], model_obj: _M, all_fields: List[AnyField], *, copying: bool = False, ) -> List[Any]: connection = connections[queryset.db] return [ # Convert field value to db value # Use attname here to support fields with custom db_column names _get_field_db_val( queryset, field, getattr(model_obj, field.attname), connection, copying=copying ) for field in all_fields ] def _format_placeholders_row( values_for_row: List[Any], all_fields: List[AnyField], connection: "DefaultConnectionProxy", *, include_cast: bool, ) -> str: placeholders = ", ".join( f"{'%s'}{f'::{field.db_type(connection)}' if include_cast else ''}" if val is not _DB_DEFAULT else "DEFAULT" for val, field in zip(values_for_row, all_fields) ) return f"({placeholders})" def _get_values_for_rows( queryset: models.QuerySet[_M], model_objs: Iterable[_M], all_fields: List[AnyField], ) -> Tuple[List[str], List[Any]]: connection = connections[queryset.db] row_values: List[str] = [] sql_args: List[Any] = [] for i, model_obj in enumerate(model_objs): values_for_row = _get_values_for_row(queryset, model_obj, all_fields) sql_args.extend((val for val in values_for_row if val is not _DB_DEFAULT)) if i == 0: row_values.append( _format_placeholders_row(values_for_row, all_fields, connection, include_cast=True) ) else: row_values.append( _format_placeholders_row( values_for_row, all_fields, connection, include_cast=False ) ) return row_values, sql_args def _get_returning_sql( returning: Union[List[str], bool], model: Type[models.Model], cursor: "CursorWrapper", include_status: bool, ) -> str: returning = returning if returning is not True else [f.column for f in _model_fields(model)] if not returning: return "" table = _quote(model._meta.db_table, cursor) returning_sql = ", ".join(f"{table}.{_quote(field, cursor)}" for field in returning) if include_status: returning_sql += ", CASE WHEN xmax = 0 THEN 'c' ELSE 'u' END AS status_" return "RETURNING " + returning_sql def _model_fields(model: Type[models.Model]) -> List["models.Field[Any, Any]"]: """Return the fields of a model, excluding generated and non-concrete ones.""" return [f for f in model._meta.fields if not getattr(f, "generated", False) and f.concrete] def _get_update_fields_sql( queryset: models.QuerySet[_M], fields: List[Union[str, UpdateField]], alias: str, ignore_unchanged: bool, cursor: "CursorWrapper", ) -> Tuple[str, str]: """Render the SET and WHERE clause of update for every update field. If the WHERE clause is returned, it means we're ignoring unchanged rows. """ connection = connections[queryset.db] model = queryset.model cols = [model._meta.get_field(update_field).column for update_field in fields] update_expressions = { f: f.expression for f in fields if isinstance(f, UpdateField) and f.expression } update_fields_expressions = {col: f"{alias}.{_quote(col, cursor)}" for col in cols} if update_expressions: connection = connections[queryset.db] compiler = SQLCompiler(query=queryset.query, connection=connection, using=queryset.using) # type: ignore with connection.cursor() as cursor: for field_name, expr in update_expressions.items(): expr = expr.resolve_expression(queryset.query, allow_joins=False, for_save=True) val = cursor.mogrify(*expr.as_sql(compiler, connection)) # type: ignore val = cast(Union[str, bytes], val) if isinstance(val, bytes): # Psycopg 2/3 return different types val = val.decode("utf-8") update_fields_expressions[model._meta.get_field(field_name).column] = val set_sql = ", ".join( f"{_quote(col, cursor)} = {update_fields_expressions[col]}" for col in cols ) ignore_unchanged_sql = "" if ignore_unchanged and cols: ignore_unchanged_sql = ("(({fields_sql}) IS DISTINCT FROM ({expressions_sql}))").format( fields_sql=", ".join( "{0}.{1}".format(_quote(model._meta.db_table, cursor), _quote(col, cursor)) for col in cols ), expressions_sql=", ".join(update_fields_expressions.values()), ) return set_sql, ignore_unchanged_sql def _get_upsert_sql( queryset: models.QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: List[Union[str, UpdateField]], returning: Union[List[str], bool], ignore_unchanged: bool, cursor: "CursorWrapper", ) -> Tuple[str, List[Any]]: """ Generates the postgres specific sql necessary to perform an upsert (ON CONFLICT) INSERT INTO table_name (field1, field2) VALUES (1, 'two') ON CONFLICT (unique_field) DO UPDATE SET field2 = EXCLUDED.field2; """ model = queryset.model # Use all fields except auto fields unless in the uniqueness constraint all_fields = [ field for field in _model_fields(model) if field.column in unique_fields or not isinstance(field, models.AutoField) ] all_field_names = [field.column for field in all_fields] all_field_names_sql = ", ".join([_quote(field, cursor) for field in all_field_names]) # Convert field names to db column names unique_db_cols = [model._meta.get_field(unique_field).column for unique_field in unique_fields] update_db_cols = [model._meta.get_field(update_field).column for update_field in update_fields] row_values, sql_args = _get_values_for_rows(queryset, model_objs, all_fields) unique_field_names_sql = ", ".join([_quote(col, cursor) for col in unique_db_cols]) update_fields_sql, ignore_unchanged_sql = _get_update_fields_sql( queryset=queryset, fields=update_fields, alias="EXCLUDED", ignore_unchanged=ignore_unchanged, cursor=cursor, ) if ignore_unchanged_sql: ignore_unchanged_sql = f"WHERE {ignore_unchanged_sql}" return_sql = _get_returning_sql(returning, model=model, cursor=cursor, include_status=True) on_conflict = ( "DO UPDATE SET {0} {1}".format(update_fields_sql, ignore_unchanged_sql) if update_db_cols else "DO NOTHING" ) row_values_sql = ", ".join(row_values) sql = ( " INSERT INTO {table_name} ({all_field_names_sql})" " VALUES {row_values_sql}" " ON CONFLICT ({unique_field_names_sql}) {on_conflict} {return_sql}" ).format( table_name=model._meta.db_table, all_field_names_sql=all_field_names_sql, row_values_sql=row_values_sql, unique_field_names_sql=unique_field_names_sql, on_conflict=on_conflict, return_sql=return_sql, ) return sql, sql_args def _upsert( queryset: models.QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef, exclude: Union[List[str], None], returning: Union[List[str], bool], ignore_unchanged: bool, cursor: "CursorWrapper", ) -> Union[UpsertResult, None]: """Internal implementation of bulk upsert.""" exclude = exclude or [] # Populate automatically generated fields in the rows like date times _fill_auto_fields(queryset, model_objs) # Sort the rows to reduce the chances of deadlock during concurrent upserts model_objs = _sort_by_unique_fields(queryset, model_objs, unique_fields) update_fields = _filter_fields(queryset, update_fields, exclude=[*exclude, *unique_fields]) # type: ignore upserted: List["Row"] = [] if model_objs: sql, sql_args = _get_upsert_sql( queryset, model_objs, unique_fields=unique_fields, update_fields=update_fields, returning=returning, ignore_unchanged=ignore_unchanged, cursor=cursor, ) sql_args = _prep_sql_args(queryset, cursor=cursor, sql_args=sql_args) cursor.execute(sql, sql_args) if cursor.description: result = [(col.name, Any) for col in cursor.description] nt_result = NamedTuple("Result", result) upserted = cast(List["Row"], [nt_result(*row) for row in cursor.fetchall()]) return UpsertResult(upserted) if returning else None def _update( queryset: models.QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None], exclude: Union[List[str], None], returning: Union[List[str], bool], ignore_unchanged: bool, cursor: "CursorWrapper", ) -> Union[List["Row"], None]: """ Core update implementation """ model = queryset.model connection = connections[queryset.db] alias = "new_values" update_fields = _filter_fields(queryset, update_fields, exclude) # type: ignore update_db_cols = [model._meta.get_field(update_field).column for update_field in update_fields] # Sort the model objects to reduce the likelihood of deadlocks model_objs = sorted(model_objs, key=lambda obj: obj.pk) if not model._meta.pk: # pragma: no cover - for type-safety raise ValueError("Model must have a primary key to perform a bulk update.") # Add the pk to the value fields so we can join during the update. value_fields = [model._meta.pk.attname] + update_fields row_values = [ [ _get_field_db_val( queryset, model_obj._meta.get_field(field), getattr(model_obj, model_obj._meta.get_field(field).attname), connection, ) for field in value_fields ] for model_obj in model_objs ] # If we do not have any values or fields to update, just return if len(row_values) == 0 or len(update_fields) == 0: return None db_types = [model._meta.get_field(field).db_type(connection) for field in value_fields] value_fields_sql = ", ".join( "{field}".format(field=_quote(model._meta.get_field(field).column, cursor)) for field in value_fields ) update_fields_sql = ", ".join( "{field} = {alias}.{field}".format(field=_quote(col, cursor), alias=alias) for col in update_db_cols ) update_fields_sql, ignore_unchanged_sql = _get_update_fields_sql( queryset=queryset, fields=update_fields, alias=alias, ignore_unchanged=ignore_unchanged, cursor=cursor, ) values_sql = ", ".join( [ "({0})".format( ", ".join( [ "%s::{0}".format(db_types[i]) if not row_number and i else "%s" for i, _ in enumerate(row) ] ) ) for row_number, row in enumerate(row_values) ] ) if ignore_unchanged_sql: ignore_unchanged_sql = f"AND {ignore_unchanged_sql}" update_sql = ( "UPDATE {table} " "SET {update_fields_sql} " "FROM (VALUES {values_sql}) AS {alias} ({value_fields_sql}) " "WHERE {table}.{pk_field} = new_values.{pk_field} {ignore_unchanged_sql} " "{returning_sql}" ).format( table=_quote(model._meta.db_table, cursor), pk_field=_quote(model._meta.pk.column, cursor), alias=alias, update_fields_sql=update_fields_sql, values_sql=values_sql, value_fields_sql=value_fields_sql, ignore_unchanged_sql=ignore_unchanged_sql, returning_sql=_get_returning_sql( returning=returning, model=model, include_status=False, cursor=cursor ), ) update_sql_params = list(itertools.chain(*row_values)) update_sql_params = _prep_sql_args(queryset, cursor=cursor, sql_args=update_sql_params) cursor.execute(update_sql, update_sql_params) updated: List["Row"] = [] if cursor.description: result = [(col.name, Any) for col in cursor.description] nt_result = NamedTuple("Result", result) updated = cast(List["Row"], [nt_result(*row) for row in cursor.fetchall()]) return updated if returning else None @overload def update( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], Literal[True]], ignore_unchanged: bool = False, ) -> List["Row"]: ... @overload def update( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Literal[False] = False, ignore_unchanged: bool = False, ) -> None: ... @overload def update( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[List["Row"], None]: ... def update( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[List["Row"], None]: """ Performs a bulk update. Args: queryset: A model or a queryset for the table being updated. model_objs: Model object values to use for the update. update_fields: A list of fields on the model objects to update. If `None`, all fields will be updated. exclude: A list of fields to exclude from the update. This is useful when `update_fields` is `None` and you want to exclude fields from being updated. returning: If True, returns all fields. If a list, only returns fields in the list. If False, do not return results from the upsert. ignore_unchanged: Ignore unchanged rows in updates. Note: Model signals such as `post_save` are not emitted. Returns: If `returning=True`, an iterable list of all updated objects. """ queryset = queryset if isinstance(queryset, models.QuerySet) else queryset.objects.all() with connections[queryset.db].cursor() as cursor: return _update( queryset=queryset, model_objs=model_objs, update_fields=update_fields, exclude=exclude, returning=returning, ignore_unchanged=ignore_unchanged, cursor=cursor, ) @overload async def aupdate( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], Literal[True]], ignore_unchanged: bool = False, ) -> List["Row"]: ... @overload async def aupdate( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Literal[False] = False, ignore_unchanged: bool = False, ) -> None: ... @overload async def aupdate( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[List["Row"], None]: ... async def aupdate( queryset: QuerySet[_M], model_objs: Iterable[_M], update_fields: Union[List[str], None] = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[List["Row"], None]: """ Perform an asynchronous bulk update. See [pgbulk.update][] Note: Like other async Django ORM methods, `aupdate` currently wraps `update` in a `sync_to_async` wrapper. It does not yet use an asynchronous database driver but will in the future. """ return await sync_to_async(update)( queryset, model_objs, update_fields=update_fields, exclude=exclude, returning=returning, ignore_unchanged=ignore_unchanged, ) @overload def upsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], Literal[True]], ignore_unchanged: bool = False, ) -> UpsertResult: ... @overload def upsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Literal[False] = False, ignore_unchanged: bool = False, ) -> None: ... @overload def upsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[UpsertResult, None]: ... def upsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[UpsertResult, None]: """ Perform a bulk upsert. Args: queryset: A model or a queryset for the table being upserted. model_objs: An iterable of Django models to upsert. All models in this list will be bulk upserted. unique_fields: A list of fields that define the uniqueness of the model. The model must have a unique constraint on these fields. update_fields: A list of fields to update whenever objects already exist. If an empty list is provided, it is equivalent to doing a bulk insert on the objects that don't exist. If `None`, all fields will be updated. If you want to perform an expression such as an `F` object on a field when it is updated, use the [pgbulk.UpdateField][] class. See examples below. exclude: A list of fields to exclude from the upsert. This is useful when `update_fields` is `None` and you want to exclude fields from being updated. This is additive to the `unique_fields` list. returning: If True, returns all fields. If a list, only returns fields in the list. If False, do not return results from the upsert. ignore_unchanged: Ignore unchanged rows in updates. Returns: If `returning=True`, the upserted result, an iterable list of all upsert objects. Use the `.updated` and `.created` attributes to iterate over created or updated elements. Note: Model signals such as `post_save` are not emitted. """ queryset = queryset if isinstance(queryset, models.QuerySet) else queryset.objects.all() with connections[queryset.db].cursor() as cursor: return _upsert( queryset, model_objs, unique_fields=unique_fields, update_fields=update_fields, returning=returning, exclude=exclude, ignore_unchanged=ignore_unchanged, cursor=cursor, ) @overload async def aupsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], Literal[True]], ignore_unchanged: bool = False, ) -> UpsertResult: ... @overload async def aupsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Literal[False] = False, ignore_unchanged: bool = False, ) -> None: ... @overload async def aupsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[UpsertResult, None]: ... async def aupsert( queryset: QuerySet[_M], model_objs: Iterable[_M], unique_fields: List[str], update_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, returning: Union[List[str], bool] = False, ignore_unchanged: bool = False, ) -> Union[UpsertResult, None]: """ Perform an asynchronous bulk upsert. See [pgbulk.upsert][] Note: Like other async Django ORM methods, `aupsert` currently wraps `upsert` in a `sync_to_async` wrapper. It does not yet use an asynchronous database driver but will in the future. """ return await sync_to_async(upsert)( queryset, model_objs, unique_fields=unique_fields, update_fields=update_fields, returning=returning, exclude=exclude, ignore_unchanged=ignore_unchanged, ) def _postgres_types_for_fields( fields: List["models.Field[Any, Any]"], connection: "DefaultConnectionProxy", ) -> List[str]: def _simplify_type(field_type: str) -> str: # Remove any size/precision/scale information, as Psycopg doesn't accept it. return _PRECISION_SPECIFIER_RE.sub("", field_type) return [_simplify_type(field.db_type(connection=connection)) for field in fields] def copy( queryset: QuerySet[_M], model_objs: Iterable[_M], copy_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, binary: bool = False, ) -> None: """ Copy data into a table. Args: queryset: queryset: A model or a queryset for the table being copied into. model_objs: An iterable of Django models to copy. copy_fields: A list of fields on the model objects to copy. If `None`, all fields will be copied. exclude: A list of fields to exclude from the copy. This is useful when `copy_fields` is `None` and you want to exclude fields from being copied. binary: If True, copy data in binary format. This can yield improved performance for large datasets. Note: Model signals such as `post_save` are not emitted. """ if psycopg_maj_version == 2: # pragma: no cover raise RuntimeError("Only psycopg3 is supported for pgbulk.copy.") queryset = queryset if isinstance(queryset, models.QuerySet) else queryset.objects.all() model = queryset.model # Populate automatically-generated fields in the rows like date times _fill_auto_fields(queryset, model_objs) # Determine which fields should be copied fields = [ model._meta.get_field(field) for field in _filter_fields( queryset, copy_fields, exclude=exclude, exclude_non_updatable=False ) ] cols = [field.column for field in fields] connection = connections[queryset.db] with connection.cursor() as cursor: all_field_names_sql = ", ".join([_quote(col, cursor) for col in cols]) copy_sql = ( f"COPY {_quote(model._meta.db_table, cursor)} ({all_field_names_sql}) FROM STDIN" ) if binary: copy_sql += " WITH (FORMAT BINARY)" with cursor.copy(copy_sql) as copier: # type: ignore if binary: postgres_types = _postgres_types_for_fields(fields, connection) copier.set_types(postgres_types) # type: ignore for model_obj in model_objs: row = _get_values_for_row(queryset, model_obj, fields, copying=True) copier.write_row(row) # type: ignore async def acopy( queryset: QuerySet[_M], model_objs: Iterable[_M], copy_fields: UpdateFieldsTypeDef = None, *, exclude: Union[List[str], None] = None, binary: bool = False, ) -> None: """ Asynchronously copy data into a table. See [pgbulk.copy][] Note: Like other async Django ORM methods, `acopy` currently wraps `copy` in a `sync_to_async` wrapper. It does not yet use an asynchronous database driver but will in the future. """ return await sync_to_async(copy)( queryset, model_objs, copy_fields=copy_fields, exclude=exclude, binary=binary, ) django-pgbulk-3.2.2/pgbulk/py.typed000066400000000000000000000000001473561042600172230ustar00rootroot00000000000000django-pgbulk-3.2.2/pgbulk/tests/000077500000000000000000000000001473561042600167005ustar00rootroot00000000000000django-pgbulk-3.2.2/pgbulk/tests/__init__.py000066400000000000000000000000001473561042600207770ustar00rootroot00000000000000django-pgbulk-3.2.2/pgbulk/tests/models.py000066400000000000000000000064261473561042600205450ustar00rootroot00000000000000from django import __version__ as DJANGO_VERSION from django.contrib.postgres.fields import ArrayField from django.db import models from django_hashids import HashidsField from timezone_field import TimeZoneField class TestFuncFieldModel(models.Model): my_key = models.CharField(unique=True, max_length=32) int_val = models.IntegerField() other_int_val = models.IntegerField(default=1) class TestAutoFieldModel(models.Model): id = models.AutoField(primary_key=True) created_at = models.DateTimeField(auto_now_add=True) widget = models.TextField(unique=True) data = models.TextField() class TestModel(models.Model): """ A model for testing manager utils. """ int_field = models.IntegerField(null=True, unique=True) char_field = models.CharField(max_length=128, null=True) float_field = models.FloatField(null=True) json_field = models.JSONField(default=dict) array_field = ArrayField(models.CharField(max_length=128), default=list) time_zone = TimeZoneField(default="UTC") class Meta: unique_together = ("int_field", "char_field") class TestUniqueTzModel(models.Model): """ A model for testing manager utils with a timezone field as the uniqueness constraint. """ int_field = models.IntegerField(null=True, unique=True) char_field = models.CharField(max_length=128, null=True) float_field = models.FloatField(null=True) time_zone = TimeZoneField(unique=True) class Meta: unique_together = ("int_field", "char_field") class TestAutoDateTimeModel(models.Model): """ A model to test that upserts work with auto_now and auto_now_add """ int_field = models.IntegerField(unique=True) auto_now_field = models.DateTimeField(auto_now=True) auto_now_add_field = models.DateTimeField(auto_now_add=True) class TestNonConcreteField(models.Model): """A model to test non-concrete fields.""" hashid = HashidsField(real_field_name="id") int_field = models.IntegerField(unique=True) class TestForeignKeyModel(models.Model): """ A test model that has a foreign key. """ int_field = models.IntegerField() test_model = models.ForeignKey(TestModel, on_delete=models.CASCADE) class TestPkForeignKey(models.Model): """ A test model with a primary key thats a foreign key to another model. """ my_key = models.ForeignKey(TestModel, primary_key=True, on_delete=models.CASCADE) char_field = models.CharField(max_length=128, null=True) class TestPkChar(models.Model): """ A test model with a primary key that is a char field. """ my_key = models.CharField(max_length=128, primary_key=True) char_field = models.CharField(max_length=128, null=True) if DJANGO_VERSION >= "5.0": from django.db.models.functions import Now class TestDbDefaultModel(models.Model): """A model to test that database defaults are not upserted""" int_field = models.IntegerField(db_default=1) char_field = models.CharField(max_length=128, db_default="test") created_at = models.DateTimeField(db_default=Now()) class TestDbDefaultModelWithOrmDefault(models.Model): """A model to test that a db default can be paired with an ORM default.""" int_field = models.IntegerField(db_default=1, default=1) django-pgbulk-3.2.2/pgbulk/tests/test_core.py000066400000000000000000001404421473561042600212460ustar00rootroot00000000000000import datetime as dt import ddf import freezegun import pytest from asgiref.sync import async_to_sync from django import __version__ as DJANGO_VERSION from django.db import transaction from django.db.models import F from pytz import timezone import pgbulk from pgbulk.core import psycopg_maj_version from pgbulk.tests import models @pytest.mark.django_db def test_non_concrete_field(): """Tests upserts and updates on non-concrete fields""" upsert_results = pgbulk.upsert( models.TestNonConcreteField, [models.TestNonConcreteField(int_field=1)], ["int_field"], returning=True, ) assert len(upsert_results.created) == 1 assert not upsert_results.updated non_concrete = models.TestNonConcreteField.objects.get() non_concrete.int_field = 2 pgbulk.update(models.TestNonConcreteField, [non_concrete]) @pytest.mark.django_db def test_func_field_upsert(): """Tests the effects of setting a field to upsert using an F object""" models.TestFuncFieldModel.objects.create(my_key="a", int_val=0) pgbulk.upsert( models.TestFuncFieldModel, [models.TestFuncFieldModel(my_key="a", int_val=0)], ["my_key"], [pgbulk.UpdateField("int_val", expression=F("int_val") + 1)], ) assert models.TestFuncFieldModel.objects.count() == 1 assert models.TestFuncFieldModel.objects.get().int_val == 1 ret = pgbulk.upsert( models.TestFuncFieldModel, [models.TestFuncFieldModel(my_key="a", int_val=0)], ["my_key"], [pgbulk.UpdateField("int_val", expression=F("int_val") - 3)], ignore_unchanged=True, returning=True, ) assert models.TestFuncFieldModel.objects.count() == 1 assert models.TestFuncFieldModel.objects.get().int_val == -2 assert len(ret.updated) == 1 ret = pgbulk.upsert( models.TestFuncFieldModel, [models.TestFuncFieldModel(my_key="a", int_val=-2)], ["my_key"], [pgbulk.UpdateField("int_val")], ignore_unchanged=True, returning=True, ) assert models.TestFuncFieldModel.objects.count() == 1 assert models.TestFuncFieldModel.objects.get().int_val == -2 assert len(ret.updated) == 0 ret = pgbulk.upsert( models.TestFuncFieldModel, [models.TestFuncFieldModel(my_key="b", int_val=0)], ["my_key"], [pgbulk.UpdateField("int_val", expression=F("int_val") - 3)], returning=True, ) assert len(ret.created) == 1 ret = pgbulk.upsert( models.TestFuncFieldModel, [ models.TestFuncFieldModel(my_key="a", int_val=0), models.TestFuncFieldModel(my_key="b", int_val=0), ], ["my_key"], [pgbulk.UpdateField("int_val", expression=F("int_val") - 3)], returning=True, ) assert models.TestFuncFieldModel.objects.get(my_key="a").int_val == -5 assert models.TestFuncFieldModel.objects.get(my_key="b").int_val == -3 ret = pgbulk.upsert( models.TestFuncFieldModel, [ models.TestFuncFieldModel(my_key="a", int_val=0), models.TestFuncFieldModel(my_key="b", int_val=0), ], ["my_key"], [ pgbulk.UpdateField("int_val", expression=F("int_val") - 1), pgbulk.UpdateField("other_int_val", expression=F("int_val") - 2), ], returning=True, ) assert models.TestFuncFieldModel.objects.get(my_key="a").int_val == -6 assert models.TestFuncFieldModel.objects.get(my_key="a").other_int_val == -7 assert models.TestFuncFieldModel.objects.get(my_key="b").int_val == -4 assert models.TestFuncFieldModel.objects.get(my_key="b").other_int_val == -5 @pytest.mark.django_db def test_auto_field_upsert(): """Verifies that upsert works with custom AutoFields""" pgbulk.upsert( models.TestAutoFieldModel, [models.TestAutoFieldModel(widget="widget", data="data")], [pgbulk.UpdateField("widget")], ["data"], ) assert models.TestAutoFieldModel.objects.count() == 1 @pytest.mark.django_db def test_upsert_return_upserts_none(): """ Tests the return_upserts flag on bulk upserts when there is no data. """ return_values = pgbulk.upsert( models.TestModel, [], ["float_field"], ["float_field"], returning=True ) assert not return_values @pytest.mark.django_db def test_upsert_return_multi_unique_fields_not_supported(): """ The new manager utils supports returning bulk upserts when there are multiple unique fields. """ return_values = pgbulk.upsert( models.TestModel, [], ["float_field", "int_field"], ["float_field"], returning=True, ) assert not return_values @pytest.mark.django_db def test_upsert_return_created_values(): """ Tests that values that are created are returned properly when returning is True. """ results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=1), models.TestModel(int_field=3), models.TestModel(int_field=4), ], ["int_field"], ["float_field"], returning=True, ) assert len(results.created) == 3 for test_model, expected_int in zip( sorted(results.created, key=lambda k: k.int_field), [1, 3, 4] ): assert test_model.int_field == expected_int assert test_model.id is not None assert models.TestModel.objects.count() == 3 @pytest.mark.django_db def test_upsert_return_list_of_values(): """ Tests that values that are created are returned properly when returning is True. Set returning to a list of fields """ results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=1, float_field=2), models.TestModel(int_field=3, float_field=4), models.TestModel(int_field=4, float_field=5), ], ["int_field"], ["float_field"], returning=["float_field"], ) assert len(results.created) == 3 with pytest.raises(AttributeError): results.created[0].int_field # noqa assert {2, 4, 5} == {m.float_field for m in results.created} @pytest.mark.django_db def test_upsert_return_created_updated_values(): """ Tests returning values when the items are either updated or created. """ # Create an item that will be updated ddf.G(models.TestModel, int_field=2, float_field=1.0) results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=1, float_field=3.0), models.TestModel(int_field=2.0, float_field=3.0), models.TestModel(int_field=3, float_field=3.0), models.TestModel(int_field=4, float_field=3.0), ], ["int_field"], ["float_field"], returning=True, ) created = results.created updated = results.updated assert len(created) == 3 assert len(updated) == 1 for test_model, expected_int in zip(sorted(created, key=lambda k: k.int_field), [1, 3, 4]): assert test_model.int_field == expected_int assert test_model.float_field == 3.0 # almostequals assert test_model.id is not None assert updated[0].int_field == 2 assert updated[0].float_field == 3.0 # almostequals assert updated[0].id is not None assert models.TestModel.objects.count() == 4 @pytest.mark.django_db def test_upsert_created_updated_auto_datetime_values(): """ Tests when the items are either updated or created when auto_now and auto_now_add datetime values are used """ # Create an item that will be updated with freezegun.freeze_time("2018-09-01 00:00:00"): ddf.G(models.TestAutoDateTimeModel, int_field=1) with freezegun.freeze_time("2018-09-02 00:00:00"): results = pgbulk.upsert( models.TestAutoDateTimeModel, [ models.TestAutoDateTimeModel(int_field=1), models.TestAutoDateTimeModel(int_field=2), models.TestAutoDateTimeModel(int_field=3), models.TestAutoDateTimeModel(int_field=4), ], ["int_field"], returning=True, ) assert len(results.created) == 3 assert len(results.updated) == 1 expected_auto_now = [ dt.datetime(2018, 9, 2), dt.datetime(2018, 9, 2), dt.datetime(2018, 9, 2), dt.datetime(2018, 9, 2), ] expected_auto_now_add = [ dt.datetime(2018, 9, 1), dt.datetime(2018, 9, 2), dt.datetime(2018, 9, 2), dt.datetime(2018, 9, 2), ] for i, test_model in enumerate(sorted(results, key=lambda k: k.int_field)): assert test_model.auto_now_field == expected_auto_now[i] assert test_model.auto_now_add_field == expected_auto_now_add[i] @pytest.mark.django_db def test_upsert_wo_update_fields(): """ Tests bulk_upsert with no update fields. This function in turn should just do a bulk create for any models that do not already exist. """ # Create models that already exist ddf.G(models.TestModel, int_field=1, float_field=1) ddf.G(models.TestModel, int_field=2, float_field=2) # Perform a bulk_upsert with one new model pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=1, float_field=3), models.TestModel(int_field=2, float_field=3), models.TestModel(int_field=3, float_field=3), ], ["int_field"], update_fields=[], ) # Three objects should now exist, but no float fields should be updated assert models.TestModel.objects.count() == 3 for test_model, expected_int_value in zip( models.TestModel.objects.order_by("int_field"), [1, 2, 3] ): assert test_model.int_field == expected_int_value assert test_model.float_field == expected_int_value @pytest.mark.django_db def test_upsert_w_blank_arguments(): """ Tests using required arguments and using blank arguments for everything else. """ pgbulk.upsert(models.TestModel, [], ["field"], ["field"]) assert not models.TestModel.objects.exists() @pytest.mark.django_db def test_upsert_no_updates(): """ Tests the case when no updates were previously stored (i.e objects are only created) """ pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], ["char_field", "float_field"], ) for i, model_obj in enumerate(models.TestModel.objects.order_by("int_field")): assert model_obj.int_field == i assert model_obj.char_field == str(i) assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_update_fields_returning(): """ Tests the case when all updates were previously stored and the int field is used as a uniqueness constraint. Assert returned values are expected and that it updates all fields by default """ # Create previously stored test models with a unique int field and -1 for # all other fields test_models = [ ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) for i in range(3) ] # Update using the int field as a uniqueness constraint results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], returning=True, ) assert results.created == [] assert {u.id for u in results.updated} == {t.id for t in test_models} assert {u.int_field for u in results.updated} == {0, 1, 2} assert {u.float_field for u in results.updated} == {0, 1, 2} assert {u.char_field for u in results.updated} == {"0", "1", "2"} @pytest.mark.django_db def test_upsert_no_update_fields_returning(): """ Tests the case when all updates were previously stored and the int field is used as a uniqueness constraint. This test does not update any fields """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], [], returning=True, ) assert list(results) == [] @pytest.mark.django_db def test_upsert_update_duplicate_fields_returning_none_updated(): """ Tests the case when all updates were previously stored and the upsert tries to update the rows with duplicate values. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="-1", float_field=-1), models.TestModel(int_field=1, char_field="-1", float_field=-1), models.TestModel(int_field=2, char_field="-1", float_field=-1), ], ["int_field"], ["char_field", "float_field"], returning=True, ignore_unchanged=True, ) assert list(results) == [] @pytest.mark.django_db def test_upsert_update_duplicate_fields_returning_some_updated(): """ Tests the case when all updates were previously stored and the upsert tries to update the rows with duplicate values. Test when some aren't duplicates """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="-1", float_field=-1), models.TestModel(int_field=1, char_field="-1", float_field=-1), models.TestModel(int_field=2, char_field="0", float_field=-1), models.TestModel(int_field=3, char_field="3", float_field=3), ], ["int_field"], ["char_field", "float_field"], returning=["char_field"], ignore_unchanged=True, ) assert len(results.updated) == 1 assert len(results.created) == 1 assert results.updated[0].char_field == "0" assert results.created[0].char_field == "3" @pytest.mark.django_db def test_upsert_update_duplicate_fields_returning_some_updated_ignore_dups(): """ Tests the case when all updates were previously stored and the upsert tries to update the rows with duplicate values. Test when some aren't duplicates. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint results = pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="-1", float_field=-1), models.TestModel(int_field=1, char_field="-1", float_field=-1), models.TestModel(int_field=2, char_field="0", float_field=-1), models.TestModel(int_field=3, char_field="3", float_field=3), ], ["int_field"], ["char_field", "float_field"], returning=["char_field"], ) assert len(results.updated) == 3 assert len(results.created) == 1 assert results.created[0].char_field == "3" @pytest.mark.django_db def test_upsert_all_updates_unique_int_field(): """ Tests the case when all updates were previously stored and the int field is used as a uniqueness constraint. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], ["char_field", "float_field"], ) # Verify that the fields were updated assert models.TestModel.objects.count() == 3 for i, model_obj in enumerate(models.TestModel.objects.order_by("int_field")): assert model_obj.int_field == i assert model_obj.char_field == str(i) assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_all_updates_unique_int_field_update_float_field(): """ Tests the case when all updates were previously stored and the int field is used as a uniqueness constraint. Only updates the float field """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], update_fields=["float_field"], ) # Verify that the float field was updated assert models.TestModel.objects.count() == 3 for i, model_obj in enumerate(models.TestModel.objects.order_by("int_field")): assert model_obj.int_field == i assert model_obj.char_field == "-1" assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_some_updates_unique_int_field_update_float_field(): """ Tests the case when some updates were previously stored and the int field is used as a uniqueness constraint. Only updates the float field. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(2): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint. The first two # are updated while the third is created pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], ["float_field"], ) # Verify that the float field was updated for the first two models and # the char field was not updated for the first two. The char field, # however, should be '2' for the third model since it was created assert models.TestModel.objects.count() == 3 for i, model_obj in enumerate(models.TestModel.objects.order_by("int_field")): assert model_obj.int_field == i assert model_obj.char_field == "-1" if i < 2 else "2" assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_some_updates_unique_timezone_field_update_float_field(): """ Tests the case when some updates were previously stored and the timezone field is used as a uniqueness constraint. Only updates the float field. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in ["US/Eastern", "US/Central"]: ddf.G( models.TestUniqueTzModel, time_zone=i, char_field="-1", float_field=-1, ) # Update using the int field as a uniqueness constraint. The first two # are updated while the third is created pgbulk.upsert( models.TestUniqueTzModel, [ models.TestModel(time_zone=timezone("US/Eastern"), char_field="0", float_field=0), models.TestModel(time_zone=timezone("US/Central"), char_field="1", float_field=1), models.TestModel(time_zone=timezone("UTC"), char_field="2", float_field=2), ], ["time_zone"], ["float_field"], ) # Verify that the float field was updated for the first two models and # the char field was not updated for the first two. The char field, # however, should be '2' for the third model since it was created m1 = models.TestUniqueTzModel.objects.get(time_zone=timezone("US/Eastern")) assert m1.char_field == "-1" assert m1.float_field == 0 # almostequals m2 = models.TestUniqueTzModel.objects.get(time_zone=timezone("US/Central")) assert m2.char_field == "-1" assert m2.float_field == 1 # almostequals m3 = models.TestUniqueTzModel.objects.get(time_zone=timezone("UTC")) assert m3.char_field == "2" assert m3.float_field == 2 # almostequals @pytest.mark.django_db def test_upsert_some_updates_unique_int_char_field_update_float_field(): """ Tests the case when some updates were previously stored and the int and char fields are used as a uniqueness constraint. Only updates the float field. """ # Create previously stored test models with a unique int and char field for i in range(2): ddf.G(models.TestModel, int_field=i, char_field=str(i), float_field=-1) # Update using the int field as a uniqueness constraint. The first two # are updated while the third is created pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field", "char_field"], ["float_field"], ) # Verify that the float field was updated for the first two models and # the char field was not updated for the first two. The char field, # however, should be '2' for the third model since it was created assert models.TestModel.objects.count() == 3 for i, model_obj in enumerate(models.TestModel.objects.order_by("int_field")): assert model_obj.int_field == i assert model_obj.char_field == str(i) assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_no_updates_unique_int_char_field(): """ Tests the case when no updates were previously stored and the int and char fields are used as a uniqueness constraint. In this case, there is data previously stored, but the uniqueness constraints don't match. """ # Create previously stored test models with a unique int field and -1 for # all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int and char field as a uniqueness constraint. All # three objects are created pgbulk.upsert( models.TestModel, [ models.TestModel(int_field=3, char_field="0", float_field=0), models.TestModel(int_field=4, char_field="1", float_field=1), models.TestModel(int_field=5, char_field="2", float_field=2), ], ["int_field", "char_field"], ["float_field"], ) # Verify that no updates occured assert models.TestModel.objects.count() == 6 assert models.TestModel.objects.filter(char_field="-1").count() == 3 for i, model_obj in enumerate( models.TestModel.objects.filter(char_field="-1").order_by("int_field") ): assert model_obj.int_field == i assert model_obj.char_field == "-1" assert model_obj.float_field == -1 # almostequals assert models.TestModel.objects.exclude(char_field="-1").count() == 3 for i, model_obj in enumerate( models.TestModel.objects.exclude(char_field="-1").order_by("int_field") ): assert model_obj.int_field == i + 3 assert model_obj.char_field == str(i) assert model_obj.float_field == i # almostequals @pytest.mark.django_db def test_upsert_some_updates_unique_int_char_field_queryset(): """ Tests the case when some updates were previously stored and a queryset is used on the bulk upsert. """ # Create previously stored test models with a unique int field and -1 # for all other fields for i in range(3): ddf.G(models.TestModel, int_field=i, char_field="-1", float_field=-1) # Update using the int field as a uniqueness constraint on a queryset. # Only one object should be updated. pgbulk.upsert( models.TestModel.objects.filter(int_field=0), [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=4, char_field="1", float_field=1), models.TestModel(int_field=5, char_field="2", float_field=2), ], ["int_field"], ["float_field"], ) # Verify that two new objecs were created assert models.TestModel.objects.count() == 5 assert models.TestModel.objects.filter(char_field="-1").count() == 3 for i, model_obj in enumerate( models.TestModel.objects.filter(char_field="-1").order_by("int_field") ): assert model_obj.int_field == i assert model_obj.char_field == "-1" @pytest.mark.django_db def test_upsert_objs_with_excluded_fields(): """ Tests that we properly exclude fields when upserting """ test_obj_1 = ddf.G( models.TestModel, int_field=1, float_field=1.0, json_field={"test": "test"}, array_field=["one", "two"], ) test_obj_2 = ddf.G( models.TestModel, int_field=2, float_field=2.0, json_field={"test2": "test2"}, array_field=["three", "four"], ) # Change the fields on the models test_obj_1.json_field = {"test": "updated"} test_obj_1.array_field = ["one", "two", "updated"] test_obj_2.json_field = {"test2": "updated"} test_obj_2.array_field = ["three", "four", "updated"] pgbulk.upsert( models.TestModel, [test_obj_1, test_obj_2], ["int_field"], None, exclude=["array_field"], ) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) # Assert that the json field was updated assert test_obj_1.json_field == {"test": "updated"} assert test_obj_2.json_field == {"test2": "updated"} # Assert that the array field was not updated assert test_obj_1.array_field == ["one", "two"] assert test_obj_2.array_field == ["three", "four"] @pytest.mark.django_db def test_update_custom_auto_field(): t_model = ddf.G(models.TestAutoFieldModel) pgbulk.update(models.TestAutoFieldModel, [t_model]) @pytest.mark.django_db def test_update_foreign_key_by_id(): t_model = ddf.G(models.TestModel) t_fk_model = ddf.G(models.TestForeignKeyModel) t_fk_model.test_model = t_model pgbulk.update(models.TestForeignKeyModel, [t_fk_model], ["test_model_id"]) assert models.TestForeignKeyModel.objects.get().test_model == t_model @pytest.mark.django_db def test_update_foreign_key_by_name(): t_model = ddf.G(models.TestModel) t_fk_model = ddf.G(models.TestForeignKeyModel) t_fk_model.test_model = t_model pgbulk.update(models.TestForeignKeyModel, [t_fk_model], ["test_model"]) assert models.TestForeignKeyModel.objects.get().test_model == t_model @pytest.mark.django_db def test_update_foreign_key_pk_using_id(): """ Tests a bulk update on a model that has a primary key to a foreign key. It uses the id of the pk in the update """ t = ddf.G(models.TestPkForeignKey, char_field="hi") pgbulk.update( models.TestPkForeignKey, [models.TestPkForeignKey(my_key_id=t.my_key_id, char_field="hello")], ["char_field"], ) assert models.TestPkForeignKey.objects.count() == 1 assert models.TestPkForeignKey.objects.filter(char_field="hello", my_key=t.my_key).exists() @pytest.mark.django_db def test_update_foreign_key_pk(): """ Tests a bulk update on a model that has a primary key to a foreign key. It uses the foreign key itself in the update """ t = ddf.G(models.TestPkForeignKey, char_field="hi") pgbulk.update( models.TestPkForeignKey, [models.TestPkForeignKey(my_key=t.my_key, char_field="hello")], ["char_field"], ) assert models.TestPkForeignKey.objects.count() == 1 assert models.TestPkForeignKey.objects.filter(char_field="hello", my_key=t.my_key).exists() @pytest.mark.django_db def test_update_char_pk(): """ Tests a bulk update on a model that has a primary key to a char field. """ ddf.G(models.TestPkChar, char_field="hi", my_key="1") pgbulk.update( models.TestPkChar, [models.TestPkChar(my_key="1", char_field="hello")], ["char_field"], ) assert models.TestPkChar.objects.count() == 1 assert models.TestPkChar.objects.filter(char_field="hello", my_key="1").exists() @pytest.mark.django_db def test_update_none(): """ Tests when no values are provided to bulk update. """ pgbulk.update(models.TestModel, [], []) @pytest.mark.django_db def test_update_no_fields_given(): """ Tests updating when no fields are given. All fields should be updated """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=2) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=3) test_obj_1.int_field = 7 test_obj_1.float_field = 8 test_obj_2.int_field = 9 test_obj_2.float_field = 10 pgbulk.update(models.TestModel, [test_obj_2, test_obj_1]) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 7 assert test_obj_1.float_field == 8 assert test_obj_2.int_field == 9 assert test_obj_2.float_field == 10 @pytest.mark.django_db def test_update_returning_ignore_unchanged(): """ Tests updating with returning and with ignore_unchanged=True """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=2) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=3) test_obj_1.int_field = 7 test_obj_1.float_field = 8 test_obj_2.int_field = 9 test_obj_2.float_field = 10 results = pgbulk.update(models.TestModel, [test_obj_2, test_obj_1], returning=True) assert len(results) == 2 results = sorted(results, key=lambda r: r.id) assert results[0].int_field == 7 assert results[0].float_field == 8 assert results[1].int_field == 9 assert results[1].float_field == 10 test_obj_1.refresh_from_db() test_obj_2.refresh_from_db() assert not pgbulk.update( models.TestModel, [test_obj_2, test_obj_1], ignore_unchanged=True, returning=True ) test_obj_1.int_field = 1 test_obj_1.float_field = 2 test_obj_2.int_field = 3 test_obj_2.float_field = 4 results = pgbulk.update(models.TestModel, [test_obj_2, test_obj_1], returning=["int_field"]) assert len(results) == 2 assert not hasattr(results[0], "float_field") assert results[0].int_field == 1 @pytest.mark.django_db def test_update_expressions(): """ Tests updating with expressions """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=2) test_obj_2 = ddf.G(models.TestModel, int_field=3, float_field=5) pgbulk.update( models.TestModel, [test_obj_1, test_obj_2], [ pgbulk.UpdateField("int_field", expression=F("int_field") + 1), pgbulk.UpdateField("float_field", expression=F("float_field") + 2), ], ) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 2 assert test_obj_1.float_field == 4 assert test_obj_2.int_field == 4 assert test_obj_2.float_field == 7 @pytest.mark.django_db def test_update_floats_to_null(): """ Tests updating a float field to a null field. """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=2) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=3) test_obj_1.float_field = None test_obj_2.float_field = None pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], ["float_field"]) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.float_field is None assert test_obj_2.float_field is None @pytest.mark.django_db def test_update_ints_to_null(): """ Tests updating an int field to a null field. """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=2) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=3) test_obj_1.int_field = None test_obj_2.int_field = None pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], ["int_field"]) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field is None assert test_obj_2.int_field is None @pytest.mark.django_db def test_update_chars_to_null(): """ Tests updating a char field to a null field. """ test_obj_1 = ddf.G(models.TestModel, int_field=1, char_field="2") test_obj_2 = ddf.G(models.TestModel, int_field=2, char_field="3") test_obj_1.char_field = None test_obj_2.char_field = None pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], ["char_field"]) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.char_field is None assert test_obj_2.char_field is None @pytest.mark.django_db def test_update_objs_no_fields_to_update(): """ Tests when objects are given to bulk update with no fields to update. Nothing should change in the objects. """ test_obj_1 = ddf.G(models.TestModel, int_field=1) test_obj_2 = ddf.G(models.TestModel, int_field=2) # Change the int fields on the models test_obj_1.int_field = 3 test_obj_2.int_field = 4 # Do a bulk update with no update fields pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], []) # The test objects int fields should be untouched test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 1 assert test_obj_2.int_field == 2 @pytest.mark.django_db def test_update_objs_one_field_to_update(): """ Tests when objects are given to bulk update with one field to update. """ test_obj_1 = ddf.G(models.TestModel, int_field=1) test_obj_2 = ddf.G(models.TestModel, int_field=2) # Change the int fields on the models test_obj_1.int_field = 3 test_obj_2.int_field = 4 # Do a bulk update with the int fields pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], ["int_field"]) # The test objects int fields should be untouched test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 3 assert test_obj_2.int_field == 4 @pytest.mark.django_db def test_update_objs_one_field_to_update_ignore_other_field(): """ Tests when objects are given to bulk update with one field to update. This test changes another field not included in the update and verifies it is not updated. """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=1.0) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=2.0) # Change the int and float fields on the models test_obj_1.int_field = 3 test_obj_2.int_field = 4 test_obj_1.float_field = 3.0 test_obj_2.float_field = 4.0 # Do a bulk update with the int fields pgbulk.update(models.TestModel, [test_obj_1, test_obj_2], ["int_field"]) # The test objects int fields should be untouched test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 3 assert test_obj_2.int_field == 4 # The float fields should not be updated assert test_obj_1.float_field == 1.0 assert test_obj_2.float_field == 2.0 @pytest.mark.django_db def test_update_objs_two_fields_to_update(): """ Tests when objects are given to bulk update with two fields to update. """ test_obj_1 = ddf.G(models.TestModel, int_field=1, float_field=1.0) test_obj_2 = ddf.G(models.TestModel, int_field=2, float_field=2.0) # Change the int and float fields on the models test_obj_1.int_field = 3 test_obj_2.int_field = 4 test_obj_1.float_field = 3.0 test_obj_2.float_field = 4.0 # Do a bulk update with the int fields pgbulk.update( models.TestModel, [test_obj_1, test_obj_2], ["int_field", "float_field"], ) # The test objects int fields should be untouched test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) assert test_obj_1.int_field == 3 assert test_obj_2.int_field == 4 # The float fields should be updated assert test_obj_1.float_field == 3.0 assert test_obj_2.float_field == 4.0 @pytest.mark.django_db def test_update_objects_with_custom_db_field_types(): """ Tests when objects are updated that have custom field types """ test_obj_1 = ddf.G( models.TestModel, int_field=1, float_field=1.0, json_field={"test": "test"}, array_field=["one", "two"], ) test_obj_2 = ddf.G( models.TestModel, int_field=2, float_field=2.0, json_field={"test2": "test2"}, array_field=["three", "four"], ) # Change the fields on the models test_obj_1.json_field = {"test": "updated"} test_obj_1.array_field = ["one", "two", "updated"] test_obj_2.json_field = {"test2": "updated"} test_obj_2.array_field = ["three", "four", "updated"] # Do a bulk update with the int fields pgbulk.update( models.TestModel, [test_obj_1, test_obj_2], ["json_field", "array_field"], ) # Refetch the objects test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) # Assert that the json field was updated assert test_obj_1.json_field == {"test": "updated"} assert test_obj_2.json_field == {"test2": "updated"} # Assert that the array field was updated assert test_obj_1.array_field, ["one", "two" == "updated"] assert test_obj_2.array_field, ["three", "four" == "updated"] @pytest.mark.django_db def test_update_objects_with_excluded_fields(): """ Tests that we properly exclude fields when updating objects """ test_obj_1 = ddf.G( models.TestModel, int_field=1, float_field=1.0, json_field={"test": "test"}, array_field=["one", "two"], ) test_obj_2 = ddf.G( models.TestModel, int_field=2, float_field=2.0, json_field={"test2": "test2"}, array_field=["three", "four"], ) # Change the fields on the models test_obj_1.json_field = {"test": "updated"} test_obj_1.array_field = ["one", "two", "updated"] test_obj_2.json_field = {"test2": "updated"} test_obj_2.array_field = ["three", "four", "updated"] pgbulk.update( models.TestModel, [test_obj_1, test_obj_2], None, exclude=["array_field"], ) test_obj_1 = models.TestModel.objects.get(id=test_obj_1.id) test_obj_2 = models.TestModel.objects.get(id=test_obj_2.id) # Assert that the json field was updated assert test_obj_1.json_field == {"test": "updated"} assert test_obj_2.json_field == {"test2": "updated"} # Assert that the array field was not updated assert test_obj_1.array_field == ["one", "two"] assert test_obj_2.array_field == ["three", "four"] @pytest.mark.django_db def test_aupsert(): """ Basic test for async upsert """ async def _run_aupsert(): return await pgbulk.aupsert( models.TestModel, [ models.TestModel(int_field=0, char_field="0", float_field=0), models.TestModel(int_field=1, char_field="1", float_field=1), models.TestModel(int_field=2, char_field="2", float_field=2), ], ["int_field"], ["char_field", "float_field"], returning=True, ) results = async_to_sync(_run_aupsert)() assert models.TestModel.objects.count() == 3 assert len(results) == 3 assert len(results.created) == 3 @pytest.mark.django_db def test_aupdate(): """ Basic test for async update """ async def _run_aupdate(t_model): await pgbulk.aupdate(models.TestModel, [t_model], ["int_field"]) t_model = models.TestModel.objects.create() t_model.int_field = 1000 async_to_sync(_run_aupdate)(t_model) assert models.TestModel.objects.get().int_field == 1000 @pytest.mark.skipif(psycopg_maj_version == 2, reason="Only run on psycopg3") @pytest.mark.django_db(transaction=True) @pytest.mark.parametrize("binary", [True, False]) def test_copy(binary: bool): """ Tests copying data into a table """ pgbulk.copy( models.TestModel, [ models.TestModel(int_field=1), models.TestModel(int_field=3), models.TestModel(int_field=4), ], binary=binary, ) assert models.TestModel.objects.count() == 3 assert set(models.TestModel.objects.values_list("int_field", flat=True)) == {1, 3, 4} @pytest.mark.skipif(psycopg_maj_version == 2, reason="Only run on psycopg3") @pytest.mark.django_db(transaction=True) @pytest.mark.parametrize("binary", [True, False]) def test_copy_with_fields(binary: bool): """ Tests copying data into a table with specific fields listed """ pgbulk.copy( models.TestModel, [ models.TestModel(int_field=1), models.TestModel(int_field=3), models.TestModel(int_field=4), ], ["int_field", "json_field", "array_field", "time_zone"], binary=binary, ) assert models.TestModel.objects.count() == 3 assert set(models.TestModel.objects.values_list("int_field", flat=True)) == {1, 3, 4} pgbulk.copy( models.TestModel, [ models.TestModel(int_field=5, float_field=1), models.TestModel(int_field=6, float_field=2), models.TestModel(int_field=7, float_field=3), ], exclude=["float_field"], ) assert models.TestModel.objects.count() == 6 assert set(models.TestModel.objects.values_list("int_field", flat=True)) == {1, 3, 4, 5, 6, 7} assert set(models.TestModel.objects.values_list("float_field", flat=True)) == {None} @pytest.mark.skipif(psycopg_maj_version == 2, reason="Only run on psycopg3") @pytest.mark.django_db @pytest.mark.parametrize("binary", [True, False]) def test_acopy(binary: bool): """ Basic test for async copy """ async def _run_acopy(): return await pgbulk.acopy( models.TestModel, [ models.TestModel(int_field=1), models.TestModel(int_field=3), models.TestModel(int_field=4), ], binary=binary, ) async_to_sync(_run_acopy)() assert models.TestModel.objects.count() == 3 assert set(models.TestModel.objects.values_list("int_field", flat=True)) == {1, 3, 4} @pytest.mark.skipif( DJANGO_VERSION < "5.0", reason="Only run on Django >= 5.0", ) @pytest.mark.django_db def test_upsert_with_db_defaults(): """ Test that we can properly upsert objects that have db defaults. """ result = pgbulk.upsert( models.TestDbDefaultModel, [models.TestDbDefaultModel(id=1)], ["id"], returning=True, ) assert len(result.created) == 1 assert result.created[0].int_field == 1 assert result.created[0].char_field == "test" assert result.created[0].created_at is not None # Insert where one row relies on the db_default, and the other does not. result = pgbulk.upsert( models.TestDbDefaultModel, [ models.TestDbDefaultModel(id=2), models.TestDbDefaultModel(id=3, int_field=3), ], ["id"], returning=True, ) assert len(result.created) == 2 assert result.created[0].int_field == 1 assert result.created[1].int_field == 3 # Test upserting an existing record without changing the db_default field # First change the int_field to something other than the db_default models.TestDbDefaultModel.objects.filter(id=1).update(int_field=42) result = pgbulk.upsert( models.TestDbDefaultModel, [models.TestDbDefaultModel(id=1)], ["id"], returning=True, ) # Should go back to the db_default value. assert result.updated[0].int_field == 1 # This should not change the int_field now. models.TestDbDefaultModel.objects.filter(id=1).update(int_field=42) result = pgbulk.upsert( models.TestDbDefaultModel, [models.TestDbDefaultModel(id=1)], ["id"], ["created_at"], returning=True, ) assert result.updated[0].int_field == 42 # Same situation but override the db_default value. result = pgbulk.upsert( models.TestDbDefaultModel, [models.TestDbDefaultModel(id=1, int_field=100)], ["id"], ["created_at"], returning=True, ) assert result.updated[0].int_field == 42 # Case in which second field isn't provided, but first and third are. # This is test positional placeholder behavior, and make sure that filling # in DEFAULT for char_field doesn't carry over to created_at. result = pgbulk.upsert( models.TestDbDefaultModel, [models.TestDbDefaultModel(id=1, int_field=100, created_at=dt.datetime(2024, 1, 1))], ["id"], returning=True, ) assert result.updated[0].int_field == 100 assert result.updated[0].char_field == "test" assert result.updated[0].created_at == dt.datetime(2024, 1, 1) @pytest.mark.skipif( DJANGO_VERSION < "5.0", reason="Only run on Django >= 5.0", ) @pytest.mark.django_db def test_update_with_db_defaults(): """ Test that we can properly update objects that have db defaults. """ obj = models.TestDbDefaultModel.objects.create() obj.int_field = 100 pgbulk.update(models.TestDbDefaultModel, [obj], ["int_field"]) assert obj.int_field == 100 obj.char_field = "updated" pgbulk.update(models.TestDbDefaultModel, [obj], ["char_field", "int_field"]) assert obj.char_field == "updated" # Int field should remain unchanged assert obj.int_field == 100 @pytest.mark.skipif( psycopg_maj_version == 2 or DJANGO_VERSION < "5.0", reason="Only run on psycopg3 and Django >= 5.0", ) @pytest.mark.django_db def test_copy_with_db_defaults(): """ Test that we cannot copy objects that have db defaults. """ with ( pytest.raises(ValueError, match="DB defaults are not supported when copying"), transaction.atomic(), ): pgbulk.copy(models.TestDbDefaultModel, [models.TestDbDefaultModel()]) # If we provide a value, this should work. pgbulk.copy(models.TestDbDefaultModel, [models.TestDbDefaultModel(int_field=1)], ["int_field"]) assert models.TestDbDefaultModel.objects.get().int_field == 1 # Additionally, an ORM paired with a db default should work. pgbulk.copy( models.TestDbDefaultModelWithOrmDefault, [models.TestDbDefaultModelWithOrmDefault()], ["int_field"], ) assert models.TestDbDefaultModelWithOrmDefault.objects.get().int_field == 1 django-pgbulk-3.2.2/pgbulk/version.py000066400000000000000000000001201473561042600175660ustar00rootroot00000000000000from importlib import metadata __version__ = metadata.version("django-pgbulk") django-pgbulk-3.2.2/poetry.lock000066400000000000000000005661721473561042600164670ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "arrow" version = "1.3.0" description = "Better dates & times for Python" optional = false python-versions = ">=3.8" files = [ {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, ] [package.dependencies] python-dateutil = ">=2.7.0" types-python-dateutil = ">=2.8.10" [package.extras] doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] [[package]] name = "asgiref" version = "3.8.1" description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.8" files = [ {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] [package.dependencies] typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] [[package]] name = "babel" version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "binaryornot" version = "0.4.4" description = "Ultra-lightweight pure Python package to check if a file is binary or text." optional = false python-versions = "*" files = [ {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, ] [package.dependencies] chardet = ">=3.0.2" [[package]] name = "black" version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "build" version = "1.2.2.post1" description = "A simple, correct Python build frontend" optional = false python-versions = ">=3.8" files = [ {file = "build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}, {file = "build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} packaging = ">=19.1" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] uv = ["uv (>=0.1.18)"] virtualenv = ["virtualenv (>=20.0.35)"] [[package]] name = "cachecontrol" version = "0.14.0" description = "httplib2 caching for requests" optional = false python-versions = ">=3.7" files = [ {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, ] [package.dependencies] filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} msgpack = ">=0.5.2,<2.0.0" requests = ">=2.16.0" [package.extras] dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] filecache = ["filelock (>=3.8.0)"] redis = ["redis (>=2.10.5)"] [[package]] name = "cachetools" version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] name = "cffi" version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] pycparser = "*" [[package]] name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "cleo" version = "2.1.0" description = "Cleo allows you to create beautiful and testable command-line interfaces." optional = false python-versions = ">=3.7,<4.0" files = [ {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"}, {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"}, ] [package.dependencies] crashtest = ">=0.4.1,<0.5.0" rapidfuzz = ">=3.0.0,<4.0.0" [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "cookiecutter" version = "1.7.3" description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "cookiecutter-1.7.3-py2.py3-none-any.whl", hash = "sha256:f8671531fa96ab14339d0c59b4f662a4f12a2ecacd94a0f70a3500843da588e2"}, {file = "cookiecutter-1.7.3.tar.gz", hash = "sha256:6b9a4d72882e243be077a7397d0f1f76fe66cf3df91f3115dbb5330e214fa457"}, ] [package.dependencies] binaryornot = ">=0.4.4" click = ">=7.0" Jinja2 = ">=2.7,<4.0.0" jinja2-time = ">=0.2.0" poyo = ">=0.5.0" python-slugify = ">=4.0.0" requests = ">=2.23.0" six = ">=1.10" [[package]] name = "coverage" version = "7.5.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "crashtest" version = "0.4.1" description = "Manage Python errors with ease" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"}, {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"}, ] [[package]] name = "cryptography" version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, ] [package.dependencies] cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] name = "distlib" version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] name = "dj-database-url" version = "2.3.0" description = "Use Database URLs in your Django Application." optional = false python-versions = "*" files = [ {file = "dj_database_url-2.3.0-py3-none-any.whl", hash = "sha256:bb0d414ba0ac5cd62773ec7f86f8cc378a9dbb00a80884c2fc08cc570452521e"}, {file = "dj_database_url-2.3.0.tar.gz", hash = "sha256:ae52e8e634186b57e5a45e445da5dc407a819c2ceed8a53d1fac004cc5288787"}, ] [package.dependencies] Django = ">=4.2" typing_extensions = ">=3.10.0.0" [[package]] name = "django" version = "4.2.11" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" files = [ {file = "Django-4.2.11-py3-none-any.whl", hash = "sha256:ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3"}, {file = "Django-4.2.11.tar.gz", hash = "sha256:6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4"}, ] [package.dependencies] asgiref = ">=3.6.0,<4" sqlparse = ">=0.3.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] [[package]] name = "django-dynamic-fixture" version = "4.0.1" description = "A full library to create dynamic model instances for testing purposes." optional = false python-versions = "*" files = [ {file = "django-dynamic-fixture-4.0.1.tar.gz", hash = "sha256:2a2197578b7702db8f5eed9ad704f6be33bac8bf0111c7c92f6063c2a4d02933"}, {file = "django_dynamic_fixture-4.0.1-py3-none-any.whl", hash = "sha256:d0611b6dc594fb1bccad1fd94dade89cc8deca12385bc2763baded3e48322547"}, ] [[package]] name = "django-hashids" version = "0.7.0" description = "Non-intrusive hashids library for Django" optional = false python-versions = ">=3.6.2,<4" files = [ {file = "django_hashids-0.7.0-py3-none-any.whl", hash = "sha256:a5d91bda97c46afa08972d9e85143af79d9e2f8d754f3d3f0df6b8ac8f57b92b"}, {file = "django_hashids-0.7.0.tar.gz", hash = "sha256:dce33e6f002308cbe03ca9ec80e27ce6b469e3abf2a42df8ba3381724683690b"}, ] [package.dependencies] hashids = ">=1.0.2" [[package]] name = "django-timezone-field" version = "4.0" description = "A Django app providing database and form fields for pytz timezone objects." optional = false python-versions = ">=3.5" files = [ {file = "django-timezone-field-4.0.tar.gz", hash = "sha256:7e3620fe2211c2d372fad54db8f86ff884098d018d56fda4dca5e64929e05ffc"}, {file = "django_timezone_field-4.0-py3-none-any.whl", hash = "sha256:758b7d41084e9ea2e89e59eb616e9b6326e6fbbf9d14b6ef062d624fe8cc6246"}, ] [package.dependencies] django = ">=2.2" pytz = "*" [[package]] name = "django-types" version = "0.19.1" description = "Type stubs for Django" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "django_types-0.19.1-py3-none-any.whl", hash = "sha256:b3f529de17f6374d41ca67232aa01330c531bbbaa3ac4097896f31ac33c96c30"}, {file = "django_types-0.19.1.tar.gz", hash = "sha256:5ae7988612cf6fbc357b018bbc3b3a878b65e04275cc46e0d35d66a708daff12"}, ] [package.dependencies] types-psycopg2 = ">=2.9.21.13" [[package]] name = "dulwich" version = "0.21.7" description = "Python Git Library" optional = false python-versions = ">=3.7" files = [ {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d4c0110798099bb7d36a110090f2688050703065448895c4f53ade808d889dd3"}, {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bc12697f0918bee324c18836053644035362bb3983dc1b210318f2fed1d7132"}, {file = "dulwich-0.21.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:471305af74790827fcbafe330fc2e8bdcee4fb56ca1177c8c481b1c8f806c4a4"}, {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54c9d0e845be26f65f954dff13a1cd3f2b9739820c19064257b8fd7435ab263"}, {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12d61334a575474e707614f2e93d6ed4cdae9eb47214f9277076d9e5615171d3"}, {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e274cebaf345f0b1e3b70197f2651de92b652386b68020cfd3bf61bc30f6eaaa"}, {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:817822f970e196e757ae01281ecbf21369383285b9f4a83496312204cf889b8c"}, {file = "dulwich-0.21.7-cp310-cp310-win32.whl", hash = "sha256:7836da3f4110ce684dcd53489015fb7fa94ed33c5276e3318b8b1cbcb5b71e08"}, {file = "dulwich-0.21.7-cp310-cp310-win_amd64.whl", hash = "sha256:4a043b90958cec866b4edc6aef5fe3c2c96a664d0b357e1682a46f6c477273c4"}, {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce8db196e79c1f381469410d26fb1d8b89c6b87a4e7f00ff418c22a35121405c"}, {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:62bfb26bdce869cd40be443dfd93143caea7089b165d2dcc33de40f6ac9d812a"}, {file = "dulwich-0.21.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c01a735b9a171dcb634a97a3cec1b174cfbfa8e840156870384b633da0460f18"}, {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa4d14767cf7a49c9231c2e52cb2a3e90d0c83f843eb6a2ca2b5d81d254cf6b9"}, {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bca4b86e96d6ef18c5bc39828ea349efb5be2f9b1f6ac9863f90589bac1084d"}, {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7b5624b02ef808cdc62dabd47eb10cd4ac15e8ac6df9e2e88b6ac6b40133673"}, {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3a539b4696a42fbdb7412cb7b66a4d4d332761299d3613d90a642923c7560e1"}, {file = "dulwich-0.21.7-cp311-cp311-win32.whl", hash = "sha256:675a612ce913081beb0f37b286891e795d905691dfccfb9bf73721dca6757cde"}, {file = "dulwich-0.21.7-cp311-cp311-win_amd64.whl", hash = "sha256:460ba74bdb19f8d498786ae7776745875059b1178066208c0fd509792d7f7bfc"}, {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4c51058ec4c0b45dc5189225b9e0c671b96ca9713c1daf71d622c13b0ab07681"}, {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4bc4c5366eaf26dda3fdffe160a3b515666ed27c2419f1d483da285ac1411de0"}, {file = "dulwich-0.21.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a0650ec77d89cb947e3e4bbd4841c96f74e52b4650830112c3057a8ca891dc2f"}, {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f18f0a311fb7734b033a3101292b932158cade54b74d1c44db519e42825e5a2"}, {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c589468e5c0cd84e97eb7ec209ab005a2cb69399e8c5861c3edfe38989ac3a8"}, {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d62446797163317a397a10080c6397ffaaca51a7804c0120b334f8165736c56a"}, {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e84cc606b1f581733df4350ca4070e6a8b30be3662bbb81a590b177d0c996c91"}, {file = "dulwich-0.21.7-cp312-cp312-win32.whl", hash = "sha256:c3d1685f320907a52c40fd5890627945c51f3a5fa4bcfe10edb24fec79caadec"}, {file = "dulwich-0.21.7-cp312-cp312-win_amd64.whl", hash = "sha256:6bd69921fdd813b7469a3c77bc75c1783cc1d8d72ab15a406598e5a3ba1a1503"}, {file = "dulwich-0.21.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d8ab29c660125db52106775caa1f8f7f77a69ed1fe8bc4b42bdf115731a25bf"}, {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d2e4485b98695bf95350ce9d38b1bb0aaac2c34ad00a0df789aa33c934469b"}, {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e138d516baa6b5bafbe8f030eccc544d0d486d6819b82387fc0e285e62ef5261"}, {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f34bf9b9fa9308376263fd9ac43143c7c09da9bc75037bb75c6c2423a151b92c"}, {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e2c66888207b71cd1daa2acb06d3984a6bc13787b837397a64117aa9fc5936a"}, {file = "dulwich-0.21.7-cp37-cp37m-win32.whl", hash = "sha256:10893105c6566fc95bc2a67b61df7cc1e8f9126d02a1df6a8b2b82eb59db8ab9"}, {file = "dulwich-0.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:460b3849d5c3d3818a80743b4f7a0094c893c559f678e56a02fff570b49a644a"}, {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74700e4c7d532877355743336c36f51b414d01e92ba7d304c4f8d9a5946dbc81"}, {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c92e72c43c9e9e936b01a57167e0ea77d3fd2d82416edf9489faa87278a1cdf7"}, {file = "dulwich-0.21.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d097e963eb6b9fa53266146471531ad9c6765bf390849230311514546ed64db2"}, {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:808e8b9cc0aa9ac74870b49db4f9f39a52fb61694573f84b9c0613c928d4caf8"}, {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1957b65f96e36c301e419d7adaadcff47647c30eb072468901bb683b1000bc5"}, {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4b09bc3a64fb70132ec14326ecbe6e0555381108caff3496898962c4136a48c6"}, {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5882e70b74ac3c736a42d3fdd4f5f2e6570637f59ad5d3e684760290b58f041"}, {file = "dulwich-0.21.7-cp38-cp38-win32.whl", hash = "sha256:29bb5c1d70eba155ded41ed8a62be2f72edbb3c77b08f65b89c03976292f6d1b"}, {file = "dulwich-0.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:25c3ab8fb2e201ad2031ddd32e4c68b7c03cb34b24a5ff477b7a7dcef86372f5"}, {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8929c37986c83deb4eb500c766ee28b6670285b512402647ee02a857320e377c"}, {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc1e11be527ac06316539b57a7688bcb1b6a3e53933bc2f844397bc50734e9ae"}, {file = "dulwich-0.21.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fc3078a1ba04c588fabb0969d3530efd5cd1ce2cf248eefb6baf7cbc15fc285"}, {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dcbd29ba30ba2c5bfbab07a61a5f20095541d5ac66d813056c122244df4ac0"}, {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8869fc8ec3dda743e03d06d698ad489b3705775fe62825e00fa95aa158097fc0"}, {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d96ca5e0dde49376fbcb44f10eddb6c30284a87bd03bb577c59bb0a1f63903fa"}, {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0064363bd5e814359657ae32517fa8001e8573d9d040bd997908d488ab886ed"}, {file = "dulwich-0.21.7-cp39-cp39-win32.whl", hash = "sha256:869eb7be48243e695673b07905d18b73d1054a85e1f6e298fe63ba2843bb2ca1"}, {file = "dulwich-0.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:404b8edeb3c3a86c47c0a498699fc064c93fa1f8bab2ffe919e8ab03eafaaad3"}, {file = "dulwich-0.21.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e598d743c6c0548ebcd2baf94aa9c8bfacb787ea671eeeb5828cfbd7d56b552f"}, {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a2d76c96426e791556836ef43542b639def81be4f1d6d4322cd886c115eae1"}, {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c88acb60a1f4d31bd6d13bfba465853b3df940ee4a0f2a3d6c7a0778c705b7"}, {file = "dulwich-0.21.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ecd315847dea406a4decfa39d388a2521e4e31acde3bd9c2609c989e817c6d62"}, {file = "dulwich-0.21.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d05d3c781bc74e2c2a2a8f4e4e2ed693540fbe88e6ac36df81deac574a6dad99"}, {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de6f8de4a453fdbae8062a6faa652255d22a3d8bce0cd6d2d6701305c75f2b3"}, {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e25953c7acbbe4e19650d0225af1c0c0e6882f8bddd2056f75c1cc2b109b88ad"}, {file = "dulwich-0.21.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4637cbd8ed1012f67e1068aaed19fcc8b649bcf3e9e26649826a303298c89b9d"}, {file = "dulwich-0.21.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:858842b30ad6486aacaa607d60bab9c9a29e7c59dc2d9cb77ae5a94053878c08"}, {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739b191f61e1c4ce18ac7d520e7a7cbda00e182c3489552408237200ce8411ad"}, {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:274c18ec3599a92a9b67abaf110e4f181a4f779ee1aaab9e23a72e89d71b2bd9"}, {file = "dulwich-0.21.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2590e9b431efa94fc356ae33b38f5e64f1834ec3a94a6ac3a64283b206d07aa3"}, {file = "dulwich-0.21.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed60d1f610ef6437586f7768254c2a93820ccbd4cfdac7d182cf2d6e615969bb"}, {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8278835e168dd097089f9e53088c7a69c6ca0841aef580d9603eafe9aea8c358"}, {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc27fb063f740712e02b4d2f826aee8bbed737ed799962fef625e2ce56e2d29"}, {file = "dulwich-0.21.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61e3451bd3d3844f2dca53f131982553be4d1b1e1ebd9db701843dd76c4dba31"}, {file = "dulwich-0.21.7.tar.gz", hash = "sha256:a9e9c66833cea580c3ac12927e4b9711985d76afca98da971405d414de60e968"}, ] [package.dependencies] urllib3 = ">=1.25" [package.extras] fastimport = ["fastimport"] https = ["urllib3 (>=1.24.1)"] paramiko = ["paramiko"] pgp = ["gpg"] [[package]] name = "exceptiongroup" version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "fastjsonschema" version = "2.20.0" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, ] [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] [[package]] name = "filelock" version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "footing" version = "0.1.6" description = "Keep templated projects in sync with their template" optional = false python-versions = "<4,>=3.8.0" files = [ {file = "footing-0.1.6-py3-none-any.whl", hash = "sha256:2ec96d9b962a33ece905ecdd9bf031b4ab70cf8f3b3f8f638dd295072423f3e8"}, {file = "footing-0.1.6.tar.gz", hash = "sha256:22649ecef2ed271c6ec1170e985e0841f2713be77c713fb0a367f924e3a9a62d"}, ] [package.dependencies] click = ">=6.7" cookiecutter = "<2.0.0" python-gitlab = ">=2.10.1" pyyaml = ">=5.1.2,<=5.3.1" requests = ">=2.13.0" tldextract = ">=3.1.2" [[package]] name = "freezegun" version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] python-dateutil = ">=2.7" [[package]] name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] [package.dependencies] python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" version = "1.2.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ {file = "griffe-1.2.0-py3-none-any.whl", hash = "sha256:a8b2fcb1ecdc5a412e646b0b4375eb20a5d2eac3a11dd8c10c56967a4097663c"}, {file = "griffe-1.2.0.tar.gz", hash = "sha256:1c9f6ef7455930f3f9b0c4145a961c90385d1e2cbc496f7796fbff560ec60d31"}, ] [package.dependencies] colorama = ">=0.4" [[package]] name = "hashids" version = "1.3.1" description = "Implements the hashids algorithm in python. For more information, visit http://hashids.org/" optional = false python-versions = ">=2.7" files = [ {file = "hashids-1.3.1-py2.py3-none-any.whl", hash = "sha256:8bddd1acba501bfc9306e7e5a99a1667f4f2cacdc20cbd70bcc5ddfa5147c94c"}, {file = "hashids-1.3.1.tar.gz", hash = "sha256:6c3dc775e65efc2ce2c157a65acb776d634cb814598f406469abef00ae3f635c"}, ] [package.extras] test = ["pytest (>=2.1.0)"] [[package]] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "importlib-metadata" version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "installer" version = "0.7.0" description = "A library for installing Python wheels." optional = false python-versions = ">=3.7" files = [ {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"}, {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, ] [[package]] name = "jaraco-classes" version = "3.4.0" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.8" files = [ {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, ] [package.dependencies] more-itertools = "*" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "jeepney" version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, ] [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] trio = ["async_generator", "trio"] [[package]] name = "jinja2" version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "jinja2-time" version = "0.2.0" description = "Jinja2 Extension for Dates and Times" optional = false python-versions = "*" files = [ {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, ] [package.dependencies] arrow = "*" jinja2 = "*" [[package]] name = "keyring" version = "24.3.1" description = "Store and access your passwords safely." optional = false python-versions = ">=3.8" files = [ {file = "keyring-24.3.1-py3-none-any.whl", hash = "sha256:df38a4d7419a6a60fea5cef1e45a948a3e8430dd12ad88b0f423c5c143906218"}, {file = "keyring-24.3.1.tar.gz", hash = "sha256:c3327b6ffafc0e8befbdb597cacdb4928ffe5c1212f7645f186e6d9957a898db"}, ] [package.dependencies] importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} "jaraco.classes" = "*" jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] completion = ["shtab (>=1.1.0)"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "markdown" version = "3.6" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" files = [ {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, ] [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "mergedeep" version = "1.3.4" description = "A deep merge function for ðŸ." optional = false python-versions = ">=3.6" files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] [[package]] name = "mkdocs" version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, ] [package.dependencies] click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" markdown = ">=3.3.6" markupsafe = ">=2.0.1" mergedeep = ">=1.3.4" mkdocs-get-deps = ">=0.2.0" packaging = ">=20.5" pathspec = ">=0.11.1" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-autorefs" version = "1.2.0" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, ] [package.dependencies] Markdown = ">=3.3" markupsafe = ">=2.0.1" mkdocs = ">=1.1" [[package]] name = "mkdocs-get-deps" version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, ] [package.dependencies] importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} mergedeep = ">=1.3.4" platformdirs = ">=2.2.0" pyyaml = ">=5.1" [[package]] name = "mkdocs-material" version = "9.5.42" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_material-9.5.42-py3-none-any.whl", hash = "sha256:452a7c5d21284b373f36b981a2cbebfff59263feebeede1bc28652e9c5bbe316"}, {file = "mkdocs_material-9.5.42.tar.gz", hash = "sha256:92779b5e9b5934540c574c11647131d217dc540dce72b05feeda088c8eb1b8f2"}, ] [package.dependencies] babel = ">=2.10,<3.0" colorama = ">=0.4,<1.0" jinja2 = ">=3.0,<4.0" markdown = ">=3.2,<4.0" mkdocs = ">=1.6,<2.0" mkdocs-material-extensions = ">=1.3,<2.0" paginate = ">=0.5,<1.0" pygments = ">=2.16,<3.0" pymdown-extensions = ">=10.2,<11.0" regex = ">=2022.4" requests = ">=2.26,<3.0" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] name = "mkdocs-material-extensions" version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, ] [[package]] name = "mkdocstrings" version = "0.26.2" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.9" files = [ {file = "mkdocstrings-0.26.2-py3-none-any.whl", hash = "sha256:1248f3228464f3b8d1a15bd91249ce1701fe3104ac517a5f167a0e01ca850ba5"}, {file = "mkdocstrings-0.26.2.tar.gz", hash = "sha256:34a8b50f1e6cfd29546c6c09fbe02154adfb0b361bb758834bf56aa284ba876e"}, ] [package.dependencies] click = ">=7.0" importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} Jinja2 = ">=2.11.1" Markdown = ">=3.6" MarkupSafe = ">=1.1" mkdocs = ">=1.4" mkdocs-autorefs = ">=1.2" platformdirs = ">=2.2" pymdown-extensions = ">=6.3" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} [package.extras] crystal = ["mkdocstrings-crystal (>=0.3.4)"] python = ["mkdocstrings-python (>=0.5.2)"] python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" version = "1.12.2" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.9" files = [ {file = "mkdocstrings_python-1.12.2-py3-none-any.whl", hash = "sha256:7f7d40d6db3cb1f5d19dbcd80e3efe4d0ba32b073272c0c0de9de2e604eda62a"}, {file = "mkdocstrings_python-1.12.2.tar.gz", hash = "sha256:7a1760941c0b52a2cd87b960a9e21112ffe52e7df9d0b9583d04d47ed2e186f3"}, ] [package.dependencies] griffe = ">=0.49" mkdocs-autorefs = ">=1.2" mkdocstrings = ">=0.26" [[package]] name = "more-itertools" version = "10.5.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" files = [ {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, ] [[package]] name = "msgpack" version = "1.1.0" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "paginate" version = "0.5.6" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" files = [ {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] ptyprocess = ">=0.5" [[package]] name = "pkginfo" version = "1.11.2" description = "Query metadata from sdists / bdists / installed packages." optional = false python-versions = ">=3.8" files = [ {file = "pkginfo-1.11.2-py3-none-any.whl", hash = "sha256:9ec518eefccd159de7ed45386a6bb4c6ca5fa2cb3bd9b71154fae44f6f1b36a3"}, {file = "pkginfo-1.11.2.tar.gz", hash = "sha256:c6bc916b8298d159e31f2c216e35ee5b86da7da18874f879798d0a1983537c86"}, ] [package.extras] testing = ["pytest", "pytest-cov", "wheel"] [[package]] name = "platformdirs" version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "poetry" version = "1.8.4" description = "Python dependency management and packaging made easy." optional = false python-versions = "<4.0,>=3.8" files = [ {file = "poetry-1.8.4-py3-none-any.whl", hash = "sha256:1223bb6dfdbdfbebc6790796b9b7a88ea1f1f4679e709594f698499010ffb129"}, {file = "poetry-1.8.4.tar.gz", hash = "sha256:5490f8da66d17eecd660e091281f8aaa5554381644540291817c249872c99202"}, ] [package.dependencies] build = ">=1.0.3,<2.0.0" cachecontrol = {version = ">=0.14.0,<0.15.0", extras = ["filecache"]} cleo = ">=2.1.0,<3.0.0" crashtest = ">=0.4.1,<0.5.0" dulwich = ">=0.21.2,<0.22.0" fastjsonschema = ">=2.18.0,<3.0.0" importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} installer = ">=0.7.0,<0.8.0" keyring = ">=24.0.0,<25.0.0" packaging = ">=23.1" pexpect = ">=4.7.0,<5.0.0" pkginfo = ">=1.10,<2.0" platformdirs = ">=3.0.0,<5" poetry-core = "1.9.1" poetry-plugin-export = ">=1.6.0,<2.0.0" pyproject-hooks = ">=1.0.0,<2.0.0" requests = ">=2.26,<3.0" requests-toolbelt = ">=1.0.0,<2.0.0" shellingham = ">=1.5,<2.0" tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.11.4,<1.0.0" trove-classifiers = ">=2022.5.19" virtualenv = ">=20.26.6,<21.0.0" xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""} [[package]] name = "poetry-core" version = "1.9.1" description = "Poetry PEP 517 Build Backend" optional = false python-versions = "<4.0,>=3.8" files = [ {file = "poetry_core-1.9.1-py3-none-any.whl", hash = "sha256:6f45dd3598e0de8d9b0367360253d4c5d4d0110c8f5c71120a14f0e0f116c1a0"}, {file = "poetry_core-1.9.1.tar.gz", hash = "sha256:7a2d49214bf58b4f17f99d6891d947a9836c9899a67a5069f52d7b67217f61b8"}, ] [[package]] name = "poetry-plugin-export" version = "1.8.0" description = "Poetry plugin to export the dependencies to various formats" optional = false python-versions = "<4.0,>=3.8" files = [ {file = "poetry_plugin_export-1.8.0-py3-none-any.whl", hash = "sha256:adbe232cfa0cc04991ea3680c865cf748bff27593b9abcb1f35fb50ed7ba2c22"}, {file = "poetry_plugin_export-1.8.0.tar.gz", hash = "sha256:1fa6168a85d59395d835ca564bc19862a7c76061e60c3e7dfaec70d50937fc61"}, ] [package.dependencies] poetry = ">=1.8.0,<3.0.0" poetry-core = ">=1.7.0,<3.0.0" [[package]] name = "poyo" version = "0.5.0" description = "A lightweight YAML Parser for Python. ðŸ“" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"}, ] [[package]] name = "psycopg2-binary" version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.8" files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392"}, {file = "psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64"}, {file = "psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-win32.whl", hash = "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8"}, {file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"}, ] [[package]] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] [[package]] name = "pycparser" version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" version = "10.8" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ {file = "pymdown_extensions-10.8-py3-none-any.whl", hash = "sha256:3539003ff0d5e219ba979d2dc961d18fcad5ac259e66c764482e8347b4c0503c"}, {file = "pymdown_extensions-10.8.tar.gz", hash = "sha256:91ca336caf414e1e5e0626feca86e145de9f85a3921a7bcbd32890b51738c428"}, ] [package.dependencies] markdown = ">=3.6" pyyaml = "*" [package.extras] extra = ["pygments (>=2.12)"] [[package]] name = "pyproject-api" version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.8" files = [ {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [package.dependencies] packaging = ">=24.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] [[package]] name = "pyproject-hooks" version = "1.2.0" description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" files = [ {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, ] [[package]] name = "pyright" version = "1.1.386" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ {file = "pyright-1.1.386-py3-none-any.whl", hash = "sha256:7071ac495593b2258ccdbbf495f1a5c0e5f27951f6b429bed4e8b296eb5cd21d"}, {file = "pyright-1.1.386.tar.gz", hash = "sha256:8e9975e34948ba5f8e07792a9c9d2bdceb2c6c0b61742b068d2229ca2bc4a9d9"}, ] [package.dependencies] nodeenv = ">=1.6.0" typing-extensions = ">=4.1" [package.extras] all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-django" version = "4.9.0" description = "A Django plugin for pytest." optional = false python-versions = ">=3.8" files = [ {file = "pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99"}, {file = "pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314"}, ] [package.dependencies] pytest = ">=7.0.0" [package.extras] docs = ["sphinx", "sphinx-rtd-theme"] testing = ["Django", "django-configurations (>=2.0)"] [[package]] name = "pytest-dotenv" version = "0.5.2" description = "A py.test plugin that parses environment files before running tests" optional = false python-versions = "*" files = [ {file = "pytest-dotenv-0.5.2.tar.gz", hash = "sha256:2dc6c3ac6d8764c71c6d2804e902d0ff810fa19692e95fe138aefc9b1aa73732"}, {file = "pytest_dotenv-0.5.2-py3-none-any.whl", hash = "sha256:40a2cece120a213898afaa5407673f6bd924b1fa7eafce6bda0e8abffe2f710f"}, ] [package.dependencies] pytest = ">=5.0.0" python-dotenv = ">=0.9.1" [[package]] name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] six = ">=1.5" [[package]] name = "python-dotenv" version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] cli = ["click (>=5.0)"] [[package]] name = "python-gitlab" version = "4.4.0" description = "A python wrapper for the GitLab API" optional = false python-versions = ">=3.8.0" files = [ {file = "python-gitlab-4.4.0.tar.gz", hash = "sha256:1d117bf7b433ae8255e5d74e72c660978f50ee85eb62248c9fb52ef43c3e3814"}, {file = "python_gitlab-4.4.0-py3-none-any.whl", hash = "sha256:cdad39d016f59664cdaad0f878f194c79cb4357630776caa9a92c1da25c8d986"}, ] [package.dependencies] requests = ">=2.25.0" requests-toolbelt = ">=0.10.1" [package.extras] autocompletion = ["argcomplete (>=1.10.0,<3)"] yaml = ["PyYaml (>=6.0.1)"] [[package]] name = "python-slugify" version = "8.0.4" description = "A Python slugify application that also handles Unicode" optional = false python-versions = ">=3.7" files = [ {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, ] [package.dependencies] text-unidecode = ">=1.3" [package.extras] unidecode = ["Unidecode (>=1.1.1)"] [[package]] name = "pytz" version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] name = "pywin32-ctypes" version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" files = [ {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, ] [[package]] name = "pyyaml" version = "5.3.1" description = "YAML parser and emitter for Python" optional = false python-versions = "*" files = [ {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] [[package]] name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] [package.dependencies] pyyaml = "*" [[package]] name = "rapidfuzz" version = "3.10.1" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.9" files = [ {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f17d9f21bf2f2f785d74f7b0d407805468b4c173fa3e52c86ec94436b338e74a"}, {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b31f358a70efc143909fb3d75ac6cd3c139cd41339aa8f2a3a0ead8315731f2b"}, {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4f43f2204b56a61448ec2dd061e26fd344c404da99fb19f3458200c5874ba2"}, {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d81bf186a453a2757472133b24915768abc7c3964194406ed93e170e16c21cb"}, {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3611c8f45379a12063d70075c75134f2a8bd2e4e9b8a7995112ddae95ca1c982"}, {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c3b537b97ac30da4b73930fa8a4fe2f79c6d1c10ad535c5c09726612cd6bed9"}, {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231ef1ec9cf7b59809ce3301006500b9d564ddb324635f4ea8f16b3e2a1780da"}, {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed4f3adc1294834955b7e74edd3c6bd1aad5831c007f2d91ea839e76461a5879"}, {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b6015da2e707bf632a71772a2dbf0703cff6525732c005ad24987fe86e8ec32"}, {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1b35a118d61d6f008e8e3fb3a77674d10806a8972c7b8be433d6598df4d60b01"}, {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc308d79a7e877226f36bdf4e149e3ed398d8277c140be5c1fd892ec41739e6d"}, {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f017dbfecc172e2d0c37cf9e3d519179d71a7f16094b57430dffc496a098aa17"}, {file = "rapidfuzz-3.10.1-cp310-cp310-win32.whl", hash = "sha256:36c0e1483e21f918d0f2f26799fe5ac91c7b0c34220b73007301c4f831a9c4c7"}, {file = "rapidfuzz-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:10746c1d4c8cd8881c28a87fd7ba0c9c102346dfe7ff1b0d021cdf093e9adbff"}, {file = "rapidfuzz-3.10.1-cp310-cp310-win_arm64.whl", hash = "sha256:dfa64b89dcb906835e275187569e51aa9d546a444489e97aaf2cc84011565fbe"}, {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:92958ae075c87fef393f835ed02d4fe8d5ee2059a0934c6c447ea3417dfbf0e8"}, {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba7521e072c53e33c384e78615d0718e645cab3c366ecd3cc8cb732befd94967"}, {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d02cbd75d283c287471b5b3738b3e05c9096150f93f2d2dfa10b3d700f2db9"}, {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efa1582a397da038e2f2576c9cd49b842f56fde37d84a6b0200ffebc08d82350"}, {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12912acee1f506f974f58de9fdc2e62eea5667377a7e9156de53241c05fdba8"}, {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666d5d8b17becc3f53447bcb2b6b33ce6c2df78792495d1fa82b2924cd48701a"}, {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26f71582c0d62445067ee338ddad99b655a8f4e4ed517a90dcbfbb7d19310474"}, {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a2ef08b27167bcff230ffbfeedd4c4fa6353563d6aaa015d725dd3632fc3de7"}, {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:365e4fc1a2b95082c890f5e98489b894e6bf8c338c6ac89bb6523c2ca6e9f086"}, {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1996feb7a61609fa842e6b5e0c549983222ffdedaf29644cc67e479902846dfe"}, {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:cf654702f144beaa093103841a2ea6910d617d0bb3fccb1d1fd63c54dde2cd49"}, {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec108bf25de674781d0a9a935030ba090c78d49def3d60f8724f3fc1e8e75024"}, {file = "rapidfuzz-3.10.1-cp311-cp311-win32.whl", hash = "sha256:031f8b367e5d92f7a1e27f7322012f3c321c3110137b43cc3bf678505583ef48"}, {file = "rapidfuzz-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f98f36c6a1bb9a6c8bbec99ad87c8c0e364f34761739b5ea9adf7b48129ae8cf"}, {file = "rapidfuzz-3.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:f1da2028cb4e41be55ee797a82d6c1cf589442504244249dfeb32efc608edee7"}, {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1340b56340896bede246f612b6ecf685f661a56aabef3d2512481bfe23ac5835"}, {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2316515169b7b5a453f0ce3adbc46c42aa332cae9f2edb668e24d1fc92b2f2bb"}, {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e06fe6a12241ec1b72c0566c6b28cda714d61965d86569595ad24793d1ab259"}, {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d99c1cd9443b19164ec185a7d752f4b4db19c066c136f028991a480720472e23"}, {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d9aa156ed52d3446388ba4c2f335e312191d1ca9d1f5762ee983cf23e4ecf6"}, {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54bcf4efaaee8e015822be0c2c28214815f4f6b4f70d8362cfecbd58a71188ac"}, {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0c955e32afdbfdf6e9ee663d24afb25210152d98c26d22d399712d29a9b976b"}, {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:191633722203f5b7717efcb73a14f76f3b124877d0608c070b827c5226d0b972"}, {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:195baad28057ec9609e40385991004e470af9ef87401e24ebe72c064431524ab"}, {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0fff4a6b87c07366662b62ae994ffbeadc472e72f725923f94b72a3db49f4671"}, {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4ffed25f9fdc0b287f30a98467493d1e1ce5b583f6317f70ec0263b3c97dbba6"}, {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d02cf8e5af89a9ac8f53c438ddff6d773f62c25c6619b29db96f4aae248177c0"}, {file = "rapidfuzz-3.10.1-cp312-cp312-win32.whl", hash = "sha256:f3bb81d4fe6a5d20650f8c0afcc8f6e1941f6fecdb434f11b874c42467baded0"}, {file = "rapidfuzz-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:aaf83e9170cb1338922ae42d320699dccbbdca8ffed07faeb0b9257822c26e24"}, {file = "rapidfuzz-3.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:c5da802a0d085ad81b0f62828fb55557996c497b2d0b551bbdfeafd6d447892f"}, {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc22d69a1c9cccd560a5c434c0371b2df0f47c309c635a01a913e03bbf183710"}, {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38b0dac2c8e057562b8f0d8ae5b663d2d6a28c5ab624de5b73cef9abb6129a24"}, {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fde3bbb14e92ce8fcb5c2edfff72e474d0080cadda1c97785bf4822f037a309"}, {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9141fb0592e55f98fe9ac0f3ce883199b9c13e262e0bf40c5b18cdf926109d16"}, {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:237bec5dd1bfc9b40bbd786cd27949ef0c0eb5fab5eb491904c6b5df59d39d3c"}, {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18123168cba156ab5794ea6de66db50f21bb3c66ae748d03316e71b27d907b95"}, {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b75fe506c8e02769cc47f5ab21ce3e09b6211d3edaa8f8f27331cb6988779be"}, {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da82aa4b46973aaf9e03bb4c3d6977004648c8638febfc0f9d237e865761270"}, {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c34c022d5ad564f1a5a57a4a89793bd70d7bad428150fb8ff2760b223407cdcf"}, {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e96c84d6c2a0ca94e15acb5399118fff669f4306beb98a6d8ec6f5dccab4412"}, {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e8e154b84a311263e1aca86818c962e1fa9eefdd643d1d5d197fcd2738f88cb9"}, {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:335fee93188f8cd585552bb8057228ce0111bd227fa81bfd40b7df6b75def8ab"}, {file = "rapidfuzz-3.10.1-cp313-cp313-win32.whl", hash = "sha256:6729b856166a9e95c278410f73683957ea6100c8a9d0a8dbe434c49663689255"}, {file = "rapidfuzz-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e06d99ad1ad97cb2ef7f51ec6b1fedd74a3a700e4949353871cf331d07b382a"}, {file = "rapidfuzz-3.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:8d1b7082104d596a3eb012e0549b2634ed15015b569f48879701e9d8db959dbb"}, {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:779027d3307e1a2b1dc0c03c34df87a470a368a1a0840a9d2908baf2d4067956"}, {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:440b5608ab12650d0390128d6858bc839ae77ffe5edf0b33a1551f2fa9860651"}, {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cac41a411e07a6f3dc80dfbd33f6be70ea0abd72e99c59310819d09f07d945"}, {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:958473c9f0bca250590200fd520b75be0dbdbc4a7327dc87a55b6d7dc8d68552"}, {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ef60dfa73749ef91cb6073be1a3e135f4846ec809cc115f3cbfc6fe283a5584"}, {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fbac18f2c19fc983838a60611e67e3262e36859994c26f2ee85bb268de2355"}, {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a0d519ff39db887cd73f4e297922786d548f5c05d6b51f4e6754f452a7f4296"}, {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bebb7bc6aeb91cc57e4881b222484c26759ca865794187217c9dcea6c33adae6"}, {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe07f8b9c3bb5c5ad1d2c66884253e03800f4189a60eb6acd6119ebaf3eb9894"}, {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfa48a4a2d45a41457f0840c48e579db157a927f4e97acf6e20df8fc521c79de"}, {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2cf44d01bfe8ee605b7eaeecbc2b9ca64fc55765f17b304b40ed8995f69d7716"}, {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e6bbca9246d9eedaa1c84e04a7f555493ba324d52ae4d9f3d9ddd1b740dcd87"}, {file = "rapidfuzz-3.10.1-cp39-cp39-win32.whl", hash = "sha256:567f88180f2c1423b4fe3f3ad6e6310fc97b85bdba574801548597287fc07028"}, {file = "rapidfuzz-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6b2cd7c29d6ecdf0b780deb587198f13213ac01c430ada6913452fd0c40190fc"}, {file = "rapidfuzz-3.10.1-cp39-cp39-win_arm64.whl", hash = "sha256:9f912d459e46607ce276128f52bea21ebc3e9a5ccf4cccfef30dd5bddcf47be8"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac4452f182243cfab30ba4668ef2de101effaedc30f9faabb06a095a8c90fd16"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:565c2bd4f7d23c32834652b27b51dd711814ab614b4e12add8476be4e20d1cf5"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d9747149321607be4ccd6f9f366730078bed806178ec3eeb31d05545e9e8f"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:616290fb9a8fa87e48cb0326d26f98d4e29f17c3b762c2d586f2b35c1fd2034b"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:073a5b107e17ebd264198b78614c0206fa438cce749692af5bc5f8f484883f50"}, {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39c4983e2e2ccb9732f3ac7d81617088822f4a12291d416b09b8a1eadebb3e29"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac7adee6bcf0c6fee495d877edad1540a7e0f5fc208da03ccb64734b43522d7a"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:425f4ac80b22153d391ee3f94bc854668a0c6c129f05cf2eaf5ee74474ddb69e"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65a2fa13e8a219f9b5dcb9e74abe3ced5838a7327e629f426d333dfc8c5a6e66"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75561f3df9a906aaa23787e9992b228b1ab69007932dc42070f747103e177ba8"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edd062490537e97ca125bc6c7f2b7331c2b73d21dc304615afe61ad1691e15d5"}, {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfcc8feccf63245a22dfdd16e222f1a39771a44b870beb748117a0e09cbb4a62"}, {file = "rapidfuzz-3.10.1.tar.gz", hash = "sha256:5a15546d847a915b3f42dc79ef9b0c78b998b4e2c53b252e7166284066585979"}, ] [package.extras] all = ["numpy"] [[package]] name = "regex" version = "2024.4.16" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.7" files = [ {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-file" version = "2.0.0" description = "File transport adapter for Requests" optional = false python-versions = "*" files = [ {file = "requests-file-2.0.0.tar.gz", hash = "sha256:20c5931629c558fda566cacc10cfe2cd502433e628f568c34c80d96a0cc95972"}, {file = "requests_file-2.0.0-py2.py3-none-any.whl", hash = "sha256:3e493d390adb44aa102ebea827a48717336d5268968c370eaf19abaf5cae13bf"}, ] [package.dependencies] requests = ">=1.0.0" [[package]] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, ] [package.dependencies] requests = ">=2.0.1,<3.0.0" [[package]] name = "ruff" version = "0.7.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, ] [[package]] name = "secretstorage" version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, ] [package.dependencies] cryptography = ">=2.0" jeepney = ">=0.6" [[package]] name = "setuptools" version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "sqlparse" version = "0.5.0" description = "A non-validating SQL parser." optional = false python-versions = ">=3.8" files = [ {file = "sqlparse-0.5.0-py3-none-any.whl", hash = "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"}, {file = "sqlparse-0.5.0.tar.gz", hash = "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"}, ] [package.extras] dev = ["build", "hatch"] doc = ["sphinx"] [[package]] name = "text-unidecode" version = "1.3" description = "The most basic Text::Unidecode port" optional = false python-versions = "*" files = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, ] [[package]] name = "tldextract" version = "5.1.2" description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well." optional = false python-versions = ">=3.8" files = [ {file = "tldextract-5.1.2-py3-none-any.whl", hash = "sha256:4dfc4c277b6b97fa053899fcdb892d2dc27295851ab5fac4e07797b6a21b2e46"}, {file = "tldextract-5.1.2.tar.gz", hash = "sha256:c9e17f756f05afb5abac04fe8f766e7e70f9fe387adb1859f0f52408ee060200"}, ] [package.dependencies] filelock = ">=3.0.8" idna = "*" requests = ">=2.1.0" requests-file = ">=1.4" [package.extras] release = ["build", "twine"] testing = ["black", "mypy", "pytest", "pytest-gitignore", "pytest-mock", "responses", "ruff", "syrupy", "tox", "types-filelock", "types-requests"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tomlkit" version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] [[package]] name = "tox" version = "4.23.2" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"}, {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"}, ] [package.dependencies] cachetools = ">=5.5" chardet = ">=5.2" colorama = ">=0.4.6" filelock = ">=3.16.1" packaging = ">=24.1" platformdirs = ">=4.3.6" pluggy = ">=1.5" pyproject-api = ">=1.8" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} virtualenv = ">=20.26.6" [package.extras] test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"] [[package]] name = "trove-classifiers" version = "2024.10.21.16" description = "Canonical source for classifiers on PyPI (pypi.org)." optional = false python-versions = "*" files = [ {file = "trove_classifiers-2024.10.21.16-py3-none-any.whl", hash = "sha256:0fb11f1e995a757807a8ef1c03829fbd4998d817319abcef1f33165750f103be"}, {file = "trove_classifiers-2024.10.21.16.tar.gz", hash = "sha256:17cbd055d67d5e9d9de63293a8732943fabc21574e4c7b74edf112b4928cf5f3"}, ] [[package]] name = "types-psycopg2" version = "2.9.21.20241019" description = "Typing stubs for psycopg2" optional = false python-versions = ">=3.8" files = [ {file = "types-psycopg2-2.9.21.20241019.tar.gz", hash = "sha256:bca89b988d2ebd19bcd08b177d22a877ea8b841decb10ed130afcf39404612fa"}, {file = "types_psycopg2-2.9.21.20241019-py3-none-any.whl", hash = "sha256:44d091e67732d16a941baae48cd7b53bf91911bc36888652447cf1ef0c1fb3f6"}, ] [[package]] name = "types-python-dateutil" version = "2.9.0.20240316" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, ] [[package]] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "tzdata" version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" version = "20.27.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchdog" version = "4.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.8" files = [ {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, ] [package.extras] watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "xattr" version = "1.1.0" description = "Python wrapper for extended filesystem attributes" optional = false python-versions = ">=3.8" files = [ {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef2fa0f85458736178fd3dcfeb09c3cf423f0843313e25391db2cfd1acec8888"}, {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccab735d0632fe71f7d72e72adf886f45c18b7787430467ce0070207882cfe25"}, {file = "xattr-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9013f290387f1ac90bccbb1926555ca9aef75651271098d99217284d9e010f7c"}, {file = "xattr-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcd5dfbcee73c7be057676ecb900cabb46c691aff4397bf48c579ffb30bb963"}, {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6480589c1dac7785d1f851347a32c4a97305937bf7b488b857fe8b28a25de9e9"}, {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08f61cbed52dc6f7c181455826a9ff1e375ad86f67dd9d5eb7663574abb32451"}, {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:918e1f83f2e8a072da2671eac710871ee5af337e9bf8554b5ce7f20cdb113186"}, {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0f06e0c1e4d06b4e0e49aaa1184b6f0e81c3758c2e8365597918054890763b53"}, {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a641ac038a9f53d2f696716147ca4dbd6a01998dc9cd4bc628801bc0df7f4d"}, {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e4ca0956fd11679bb2e0c0d6b9cdc0f25470cc00d8da173bb7656cc9a9cf104"}, {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6881b120f9a4b36ccd8a28d933bc0f6e1de67218b6ce6e66874e0280fc006844"}, {file = "xattr-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab29d9288aa28e68a6f355ddfc3f0a7342b40c9012798829f3e7bd765e85c2c"}, {file = "xattr-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c80bbf55339c93770fc294b4b6586b5bf8e85ec00a4c2d585c33dbd84b5006"}, {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1418705f253b6b6a7224b69773842cac83fcbcd12870354b6e11dd1cd54630f"}, {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687e7d18611ef8d84a6ecd8f4d1ab6757500c1302f4c2046ce0aa3585e13da3f"}, {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6ceb9efe0657a982ccb8b8a2efe96b690891779584c901d2f920784e5d20ae3"}, {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b489b7916f239100956ea0b39c504f3c3a00258ba65677e4c8ba1bd0b5513446"}, {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0a9c431b0e66516a078125e9a273251d4b8e5ba84fe644b619f2725050d688a0"}, {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1a5921ea3313cc1c57f2f53b63ea8ca9a91e48f4cc7ebec057d2447ec82c7efe"}, {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6ad2a7bd5e6cf71d4a862413234a067cf158ca0ae94a40d4b87b98b62808498"}, {file = "xattr-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0683dae7609f7280b0c89774d00b5957e6ffcb181c6019c46632b389706b77e6"}, {file = "xattr-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cb15cd94e5ef8a0ef02309f1bf973ba0e13c11e87686e983f371948cfee6af"}, {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff6223a854229055e803c2ad0c0ea9a6da50c6be30d92c198cf5f9f28819a921"}, {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d44e8f955218638c9ab222eed21e9bd9ab430d296caf2176fb37abe69a714e5c"}, {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:caab2c2986c30f92301f12e9c50415d324412e8e6a739a52a603c3e6a54b3610"}, {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d6eb7d5f281014cd44e2d847a9107491af1bf3087f5afeded75ed3e37ec87239"}, {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:47a3bdfe034b4fdb70e5941d97037405e3904accc28e10dbef6d1c9061fb6fd7"}, {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00d2b415cf9d6a24112d019e721aa2a85652f7bbc9f3b9574b2d1cd8668eb491"}, {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:78b377832dd0ee408f9f121a354082c6346960f7b6b1480483ed0618b1912120"}, {file = "xattr-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6461a43b585e5f2e049b39bcbfcb6391bfef3c5118231f1b15d10bdb89ef17fe"}, {file = "xattr-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24d97f0d28f63695e3344ffdabca9fcc30c33e5c8ccc198c7524361a98d526f2"}, {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad47d89968c9097900607457a0c89160b4771601d813e769f68263755516065"}, {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc53cab265f6e8449bd683d5ee3bc5a191e6dd940736f3de1a188e6da66b0653"}, {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cd11e917f5b89f2a0ad639d9875943806c6c9309a3dd02da5a3e8ef92db7bed9"}, {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c5a78c7558989492c4cb7242e490ffb03482437bf782967dfff114e44242343"}, {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cebcf8a303a44fbc439b68321408af7267507c0d8643229dbb107f6c132d389c"}, {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b0d73150f2f9655b4da01c2369eb33a294b7f9d56eccb089819eafdbeb99f896"}, {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:793c01deaadac50926c0e1481702133260c7cb5e62116762f6fe1543d07b826f"}, {file = "xattr-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e189e440bcd04ccaad0474720abee6ee64890823ec0db361fb0a4fb5e843a1bf"}, {file = "xattr-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afacebbc1fa519f41728f8746a92da891c7755e6745164bd0d5739face318e86"}, {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b1664edf003153ac8d1911e83a0fc60db1b1b374ee8ac943f215f93754a1102"}, {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda2684228798e937a7c29b0e1c7ef3d70e2b85390a69b42a1c61b2039ba81de"}, {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b735ac2625a4fc2c9343b19f806793db6494336338537d2911c8ee4c390dda46"}, {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fa6a7af7a4ada43f15ccc58b6f9adcdbff4c36ba040013d2681e589e07ae280a"}, {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1059b2f726e2702c8bbf9bbf369acfc042202a4cc576c2dec6791234ad5e948"}, {file = "xattr-1.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e2255f36ebf2cb2dbf772a7437ad870836b7396e60517211834cf66ce678b595"}, {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba4f80b9855cc98513ddf22b7ad8551bc448c70d3147799ea4f6c0b758fb466"}, {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb70c16e7c3ae6ba0ab6c6835c8448c61d8caf43ea63b813af1f4dbe83dd156"}, {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83652910ef6a368b77b00825ad67815e5c92bfab551a848ca66e9981d14a7519"}, {file = "xattr-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7a92aff66c43fa3e44cbeab7cbeee66266c91178a0f595e044bf3ce51485743b"}, {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4f71b673339aeaae1f6ea9ef8ea6c9643c8cd0df5003b9a0eaa75403e2e06c"}, {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a20de1c47b5cd7b47da61799a3b34e11e5815d716299351f82a88627a43f9a96"}, {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23705c7079b05761ff2fa778ad17396e7599c8759401abc05b312dfb3bc99f69"}, {file = "xattr-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:27272afeba8422f2a9d27e1080a9a7b807394e88cce73db9ed8d2dde3afcfb87"}, {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd43978966de3baf4aea367c99ffa102b289d6c2ea5f3d9ce34a203dc2f2ab73"}, {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded771eaf27bb4eb3c64c0d09866460ee8801d81dc21097269cf495b3cac8657"}, {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca300c0acca4f0cddd2332bb860ef58e1465d376364f0e72a1823fdd58e90d"}, {file = "xattr-1.1.0.tar.gz", hash = "sha256:fecbf3b05043ed3487a28190dec3e4c4d879b2fcec0e30bafd8ec5d4b6043630"}, ] [package.dependencies] cffi = ">=1.16.0" [package.extras] test = ["pytest"] [[package]] name = "zipp" version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<4" content-hash = "06889783092ca6b9fd17b493715a40d1a23c50bfdcd645f8f86bbc85792db26e" django-pgbulk-3.2.2/pyproject.toml000066400000000000000000000046441473561042600171760ustar00rootroot00000000000000[build-system] requires = ["poetry_core>=1.9.0"] build-backend = "poetry.core.masonry.api" [tool.coverage.run] branch = true source = ["pgbulk"] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "raise AssertionError", "raise NotImplementedError", "pass", "pytest.mark.skip", "@(typing\\.)?overload", "if TYPE_CHECKING:", ] show_missing = true fail_under = 100 [tool.poetry] name = "django-pgbulk" packages = [ { include = "pgbulk" } ] exclude = [ "*/tests/" ] version = "3.2.2" description = "Native Postgres update, upsert, and copy operations." authors = ["Wes Kendall"] classifiers = [ "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3 :: Only", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", ] license = "BSD-3-Clause" readme = "README.md" homepage = "https://github.com/AmbitionEng/django-pgbulk" repository = "https://github.com/AmbitionEng/django-pgbulk" documentation = "https://django-pgbulk.readthedocs.io" [tool.poetry.dependencies] python = ">=3.9.0,<4" django = ">=4" [tool.poetry.dev-dependencies] pytest = "8.3.3" pytest-cov = "5.0.0" pytest-dotenv = "0.5.2" django-timezone-field = "4.0" django-hashids = "0.7.0" freezegun = "1.5.1" tox = "4.23.2" ruff = "0.7.1" pyright = "1.1.386" mkdocs = "1.6.1" black = "24.10.0" mkdocs-material = "9.5.42" mkdocstrings-python = "1.12.2" footing = "*" setuptools = "*" poetry-core = "1.9.1" cleo = "2.1.0" poetry-plugin-export = "1.8.0" typing-extensions = "4.12.2" django-types = "0.19.1" dj-database-url = "2.3.0" psycopg2-binary = "2.9.10" pytest-django = "4.9.0" django-dynamic-fixture = "4.0.1" [tool.pytest.ini_options] xfail_strict = true testpaths = "pgbulk/tests" norecursedirs = ".venv" addopts = "--reuse-db" DJANGO_SETTINGS_MODULE = "settings" [tool.ruff] lint.select = ["E", "F", "B", "I", "G", "C4"] line-length = 99 target-version = "py39" [tool.pyright] exclude = [ "**/node_modules", "**/__pycache__", "src/experimental", "src/typestubs", "**/migrations/**", "**/tests/**", ] pythonVersion = "3.9" typeCheckingMode = "strict" django-pgbulk-3.2.2/settings.py000066400000000000000000000005751473561042600164730ustar00rootroot00000000000000import os import dj_database_url SECRET_KEY = "django-pgbulk" # Install the tests as an app so that we can make test models INSTALLED_APPS = [ "pgbulk", "pgbulk.tests", ] # Database url comes from the DATABASE_URL env var DATABASES = {"default": dj_database_url.config()} DEFAULT_AUTO_FIELD = "django.db.models.AutoField" USE_TZ = False DJANGO_HASHIDS_SALT = "salt" django-pgbulk-3.2.2/tox.ini000066400000000000000000000025141473561042600155670ustar00rootroot00000000000000[tox] isolated_build = true envlist = py{39,310,311,312,313}-django42-psycopg2 py313-django42-psycopg3 py{310,311,312,313}-django50-psycopg2 py313-django50-psycopg3 py{310,311,312,313}-django51-psycopg2 py313-django51-psycopg3 report [testenv] allowlist_externals = poetry bash grep skip_install = true passenv = DATABASE_URL PYTHONDONTWRITEBYTECODE install_command = pip install {opts} --no-compile {packages} deps = django42: Django>=4.2,<4.3 django50: Django>=5.0,<5.1 django51: Django>=5.1,<5.2 psycopg2: psycopg2-binary psycopg3: psycopg[binary] commands = bash -c 'poetry export --with dev --without-hashes -f requirements.txt | grep -v "^[dD]jango==" | grep -v "^psycopg2-binary==" | pip install --no-compile -q --no-deps -r /dev/stdin' pip install --no-compile -q --no-deps --no-build-isolation -e . pytest --create-db --cov --cov-fail-under=0 --cov-append --cov-config pyproject.toml {posargs} [testenv:report] allowlist_externals = coverage skip_install = true depends = py{39,310,311,312,313}-django42-psycopg2, py313-django42-psycopg3, py{310,311,312,313}-django50-psycopg2, py313-django50-psycopg3, py{310,311,312,313}-django51-psycopg2, py313-django51-psycopg3 parallel_show_output = true commands = coverage report --fail-under 100 coverage erase