├── .dockerignore ├── .githooks └── pre-commit ├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── Taskfile.yml ├── docs ├── README.md └── api.md ├── mkdocs.yml ├── pyproject.toml ├── streamlit_sql ├── __init__.py ├── create_delete_model.py ├── filters.py ├── input_fields.py ├── lib.py ├── many.py ├── params.py ├── read_cte.py ├── sql_iu.py └── update_model.py └── uv.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .~* 2 | .git/ 3 | build/ 4 | __*/ 5 | venv/ 6 | *egg-info/* 7 | *_pycache__/* 8 | .env -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$') 4 | 5 | if [ -n "$staged_files" ]; then 6 | echo "Staged python files:" 7 | echo "$staged_files" 8 | echo 9 | echo "$staged_files" | xargs -I {} pyright "{}" 10 | echo "$staged_files" | xargs -I {} pycln "{}" 11 | echo "$staged_files" | xargs -I {} isort --profile black "{}" 12 | echo "$staged_files" | xargs -I {} black "{}" 13 | set +x 14 | # Add only the formatted changes to the staging area 15 | echo "$staged_files" | xargs -I {} git add "{}" 16 | fi 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | files 3 | *#*.*# 4 | typings/* 5 | venv 6 | dbdata 7 | .env 8 | 9 | 10 | # Byte-compiled / optimized / DLL files 11 | */__pycache__/ 12 | 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | *.py,cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | cover/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | db.sqlite3-journal 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | .pybuilder/ 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | # For a library or package, you might want to ignore these files since the code is 97 | # intended to run in multiple environments; otherwise, check them in: 98 | # .python-version 99 | 100 | # pipenv 101 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 102 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 103 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 104 | # install all needed dependencies. 105 | #Pipfile.lock 106 | 107 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 108 | __pypackages__/ 109 | 110 | # Celery stuff 111 | celerybeat-schedule 112 | celerybeat.pid 113 | 114 | # SageMath parsed files 115 | *.sage.py 116 | 117 | # Environments 118 | .env 119 | .venv 120 | env/ 121 | venv/ 122 | ENV/ 123 | env.bak/ 124 | venv.bak/ 125 | 126 | # Spyder project settings 127 | .spyderproject 128 | .spyproject 129 | 130 | # Rope project settings 131 | .ropeproject 132 | 133 | # mkdocs documentation 134 | /site 135 | 136 | # mypy 137 | .mypy_cache/ 138 | .dmypy.json 139 | dmypy.json 140 | 141 | # Pyre type checker 142 | .pyre/ 143 | 144 | # pytype static type analyzer 145 | .pytype/ 146 | 147 | # Cython debug symbols 148 | cython_debug/ 149 | 150 | /hlpj/test.py 151 | /.pdbrc 152 | /docker/bin/hledger-linux-x64.zip 153 | /.task/ 154 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | # Config debian 4 | RUN apt update && apt install -y \ 5 | libgmp10 \ 6 | libtinfo6 \ 7 | curl \ 8 | unzip \ 9 | gnupg \ 10 | openssh-client \ 11 | enscript \ 12 | ghostscript \ 13 | locales \ 14 | ripgrep \ 15 | python3-cffi \ 16 | python3-brotli \ 17 | libpango-1.0-0 \ 18 | libharfbuzz0b \ 19 | libpangoft2-1.0-0 \ 20 | && rm -rf /var/lib/apt/lists/* 21 | 22 | RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ 23 | && sed -i -e 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/' /etc/locale.gen \ 24 | && locale-gen 25 | 26 | COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ 27 | WORKDIR /app 28 | 29 | RUN --mount=type=cache,target=/root/.cache/uv \ 30 | --mount=type=bind,source=uv.lock,target=uv.lock \ 31 | --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 32 | uv sync --frozen --no-install-project 33 | 34 | COPY ./streamlit_sql ./app 35 | 36 | RUN --mount=type=cache,target=/root/.cache/uv \ 37 | --mount=type=bind,source=uv.lock,target=uv.lock \ 38 | --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 39 | uv sync --frozen 40 | 41 | EXPOSE 8501 42 | ENV PYTHONPATH="/app" 43 | ENV LANG=en_US.UTF-8 44 | ENV BKP_DIR=/bkp 45 | 46 | RUN useradd -m appuser 47 | ENV HOME=/home/appuser 48 | ENV PATH=$HOME/.local/bin:$PATH 49 | USER appuser 50 | 51 | ENTRYPOINT ["uv","run", "--","streamlit", "run", "./app/webapp.py"] 52 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | remote_server := CHANGEME 2 | remote_wheel_dir=/shared/wheels # CHANGEME 3 | 4 | get_value = $(shell sed -n "s/\($1 \?= \?\)\(.*\)/\2/p" pyproject.toml) 5 | pkg_name := $(call get_value,name) 6 | modules := $(pkg_name) 7 | version := $(call get_value,version) 8 | wheel := $(pkg_name)-$(version)-py3-none-any.whl 9 | docker_image := $(pkg_name) 10 | image_file := $(pkg_name).tar 11 | remote_docker_compose := /shared/dock/$(pkg_name)/docker-compose.yml 12 | registry_docker_compose := /shared/dock/registry/docker-compose.yml 13 | 14 | 15 | define remote_command 16 | docker compose -f $(remote_docker_compose) down && \ 17 | docker compose -f $(remote_docker_compose) pull && \ 18 | docker compose -f $(remote_docker_compose) up -d && \ 19 | docker compose -f $(registry_docker_compose) exec -it registry /bin/registry garbage-collect /etc/docker/registry/config.yml && \ 20 | docker container prune -f && \ 21 | docker image prune -f 22 | endef 23 | 24 | 25 | .PHONY: docker 26 | docker: 27 | make fix && \ 28 | docker compose down && \ 29 | docker compose build && \ 30 | docker compose push && \ 31 | ssh pulsar "$(remote_command)" 32 | 33 | .PHONY: fix 34 | fix: 35 | uv run -- pyright && \ 36 | uv run -- ruff check --fix && \ 37 | uv run -- ruff format 38 | 39 | .PHONY: st 40 | st: 41 | uv run -- streamlit run app/webapp.py 42 | 43 | .PHONY: publish 44 | publish: 45 | uv build && uv publish 46 | 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # streamlit_sql 2 | 3 | ## Introduction 4 | 5 | Creating a CRUD interface can be a tedious and repetitive task. This package is intended to replace all of that with a few lines of code that involves simply creating a sqlalchemy statement and calling the main *SqlUi* class with only 3 required arguments. All extra and advanced features are available by supplying non-required arguments to the class initialization. 6 | 7 | When the main class is initialized, it will display the database table data with most of the expected features of a crud interface, so the user will be able to **read, filter, update, create and delete rows** with many useful features. 8 | 9 | It also offers useful information about the data as property like: 10 | - df: The Dataframe displayed in the screen 11 | - selected_rows: The position of selected rows. This is not the row id 12 | - qtty_rows: The quantity of all rows after filtering 13 | 14 | ## Demo 15 | 16 | See the package in action [here](https://example-crud.streamlit.app/). 17 | 18 | ## Features 19 | 20 | ### READ 21 | 22 | - Display as a regular st.dataframe 23 | - Add pagination, displaying only a set of rows each time 24 | - Set the dataframe to be displayed using standard sqlalchemy select statement, where you can JOIN, ORDER BY, WHERE, etc. 25 | - Add a column to show the rolling sum of a numeric column 26 | - Conditional styling if the DataFrame based on each row value. For instance, changing its background color 27 | - Format the number display format. 28 | - Display multiple CRUD interfaces in the same page using unique base_key. 29 | - Show *many-to-one* relation in edit forms with basic editing. 30 | - Log database modification to stderr or to your prefered loguru handler. (can be disabled) 31 | 32 | ### FILTER 33 | 34 | - Filter the data by some columns before presenting the table. 35 | - Let users filter the columns by selecting conditions in the filter expander 36 | - Give possible candidates when filtering using existing values for the columns 37 | - Let users select ForeignKey's values using the string representation of the foreign table, instead of its id number 38 | 39 | ### UPDATE 40 | 41 | - Users update rows with a dialog opened by selecting the row and clicking the icon 42 | - Text columns offers candidates from existing values 43 | - ForeignKey columns are added by the string representation instead of its id number 44 | - In Update form, list all ONE-TO-MANY related rows with pagination, where you can directly create and delete related table rows. 45 | - Log updates to database to stderr or in anyway **loguru** can handle 46 | 47 | 48 | ### CREATE 49 | 50 | - Users create new rows with a dialog opened by clicking the create button 51 | - Text columns offers candidates from existing values 52 | - Hide columns to fill by offering default values 53 | - ForeignKey columns are added by the string representation instead of its id number 54 | 55 | ### DELETE 56 | 57 | - Delete one or multiple rows by selecting in DataFrame and clicking the corresponding button. A dialog will list selected rows and confirm deletion. 58 | 59 | 60 | 61 | ## Requirements 62 | 63 | All the requirements you should probably have anyway. 64 | 65 | 1. streamlit and sqlalchemy 66 | 2. Sqlalchemy models needs a __str__ method 67 | 2. Id column should be called "id" 68 | 3. Relationships should be added for all ForeignKey columns 69 | 70 | 71 | ## Basic Usage 72 | 73 | Install the package using pip: 74 | 75 | ```bash 76 | pip install streamlit_sql 77 | ``` 78 | 79 | Run `show_sql_ui` as the example below: 80 | 81 | ```python 82 | from streamlit_sql import show_sql_ui 83 | from sqlalchemy import select 84 | 85 | conn = st.connection("sql", url="") 86 | 87 | stmt = ( 88 | select( 89 | db.Invoice.id, 90 | db.Invoice.Date, 91 | db.Invoice.amount, 92 | db.Client.name, 93 | ) 94 | .join(db.Client) 95 | .where(db.Invoice.amount > 1000) 96 | .order_by(db.Invoice.date) 97 | ) 98 | 99 | show_sql_ui(conn=conn, 100 | read_instance=stmt, 101 | edit_create_model=db.Invoice, 102 | available_filter=["name"], 103 | rolling_total_column="amount", 104 | ) 105 | 106 | show_sql_ui(conn, model_opts) 107 | ``` 108 | 109 | !!! warning 110 | In the statement, **always** include the primary_key column, that should be named *id* 111 | 112 | ### Interface 113 | 114 | - Filter: Open the "Filter" expander and fill the inputs 115 | - Add row: Click on "plus" button (no dataframe row can be selected) 116 | - Edit row: Click on "pencil" button (one and only one dataframe row should be selected) 117 | - Delete row: Click on "trash" button (one or more dataframe rows should be selected) 118 | 119 | 120 | ## Customize 121 | 122 | You can adjust the CRUD interface by the select statement you provide to *read_instance* arg and giving optional arguments to the *show_sql_ui* function. See the docstring for more information or at [documentation webpage](https://edkedk99.github.io/streamlit_sql/api/#streamlit_sql.SqlUi): 123 | 124 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | vars: 4 | PKG_NAME: 5 | sh: sed -nE 's/^name = "([^"]+)"/\1/p' pyproject.toml 6 | PKG_VERSION: 7 | sh: sed -nE 's/^version = "([^"]+)"/\1/p' pyproject.toml 8 | 9 | tasks: 10 | deploy-docs: 11 | desc: Deploy mkdocs to github 12 | cmds: 13 | - uv run mkdocs gh-deploy 14 | 15 | show-docs: 16 | desc: Serve local mkdocs 17 | cmds: 18 | - uv run mkdocs serve 19 | 20 | docker: 21 | desc: Update and rerun container on server 22 | vars: 23 | REMOTE_DOCKER_COMPOSE: /shared/dock/{{.PKG_NAME}}/docker-compose.yml 24 | REGISTRY_DOCKER_COMPOSE: /shared/dock/registry/docker-compose.yml 25 | REMOTE_COMMAND: | 26 | docker compose -f {{.REMOTE_DOCKER_COMPOSE}} down && \ 27 | docker compose -f {{.REMOTE_DOCKER_COMPOSE}} pull && \ 28 | docker compose -f {{.REMOTE_DOCKER_COMPOSE}} up -d && \ 29 | docker compose -f {{.REMOTE_DOCKER_COMPOSE}} exec -it registry /bin/registry garbage-collect /etc/docker/registry/config.yml && \ 30 | docker container prune -f && \ 31 | docker image prune -f 32 | deps: [fix] 33 | cmds: 34 | - | 35 | docker compose build && \ 36 | docker compose push && \ 37 | ssh pulsar "{{.REMOTE_COMMAND}}" 38 | 39 | publish: 40 | desc: Publish to pypi 41 | deps: [fix] 42 | cmds: 43 | - uv build && uv publish 44 | 45 | fix: 46 | desc: Run checkers on .py files 47 | cmds: 48 | - | 49 | uv run -- pyright && \ 50 | uv run -- ruff check --fix && \ 51 | uv run -- ruff format 52 | sources: 53 | - "{{.PKG_NAME}}/*.py" 54 | 55 | st: 56 | desc: Run app local 57 | cmds: 58 | - uv run -- streamlit run app/webapp.py 59 | 60 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | 2 | ## API 3 | 4 | ::: streamlit_sql 5 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: streamlit-sql 2 | site_url: https://edkedek99.github.io/streamlit_sql/ 3 | repo_url: https://github.com/edkedk99/streamlit_sql 4 | repo_name: Github 5 | remote_name: github 6 | strict: true 7 | 8 | theme: 9 | name: material 10 | palette: 11 | primary: teal 12 | features: 13 | - navigation.tabs 14 | 15 | nav: 16 | - Home: README.md 17 | - API: api.md 18 | 19 | markdown_extensions: 20 | - attr_list 21 | - toc: 22 | permalink: "#" 23 | - admonition 24 | - pymdownx.details 25 | - pymdownx.superfences 26 | 27 | plugins: 28 | - search 29 | - mkdocstrings 30 | 31 | 32 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pyright] 2 | venvPath = ".venv" 3 | venv = "." 4 | verboseOutput = false 5 | reportConstantRedefinition = "warning" 6 | reportUnnecessaryComparison = "warning" 7 | reportAssertAlwaysTrue = "warning" 8 | reportUnusedExpression = false 9 | include = ["streamlit_sql"] 10 | exclude = ["**/node_modules", 11 | "**/__pycache__", 12 | "build/*", 13 | "dist/*", 14 | ".venv/*"] 15 | 16 | [build-system] 17 | requires = ["setuptools", "wheel"] 18 | build-backend = "setuptools.build_meta" 19 | 20 | [tool.setuptools] 21 | packages = ["streamlit_sql"] 22 | 23 | [tool.vulture] 24 | exclude = [] 25 | ignore_decorators = [] 26 | ignore_names = [] 27 | make_whitelist = false 28 | min_confidence = 0 29 | paths = ["streamlit_sql"] 30 | sort_by_size = true 31 | verbose = false 32 | 33 | [tool.ruff] 34 | exclude = [".venv", "whitelist.py", "alembic", "alembic_dev"] 35 | line-length = 88 36 | 37 | [tool.ruff.lint] 38 | ignore = ["SIM108", "E501","PD901", "RET504", "PLR", "PTH123", "ISC001","ARG001","ARG002", "C901"] 39 | select = ["UP","YTT", "A", "EM", "ICN","I", "SIM", "E", "F", "C90", "B", "C4", "FA", "ISC", "INP", "PIE", "RET", "TID", "ARG", "PTH", "ERA", "PD", "PL", "FLY", "NPY", "PERF", "FURB", "RUF"] 40 | 41 | [tool.ruff.lint.pep8-naming] 42 | ignore-names = ["_self"] 43 | 44 | 45 | [metadata] 46 | description = "Streamlit SQL" 47 | keyword = [] 48 | classifiers = [""] 49 | author = "Anonymous" 50 | author_email = "" 51 | 52 | [project] 53 | name = "streamlit_sql" 54 | version = "0.3.2" 55 | description = "CRUD interface for sqlalchemy using streamlit" 56 | readme = "README.md" 57 | # readme= "docs/README.md" 58 | requires-python = ">=3.13" 59 | license = {text = "MIT License"} 60 | dependencies = [ 61 | "python-dateutil", 62 | "pandas", 63 | "sqlalchemy", 64 | "streamlit", 65 | "streamlit_datalist", 66 | "streamlit_antd_components", 67 | "loguru>=0.7.3", 68 | ] 69 | 70 | [dependency-groups] 71 | 72 | dev = [ 73 | "setuptools", 74 | "ipython", 75 | "build", 76 | "rich", 77 | "isort", 78 | "vermin", 79 | "pandas-stubs", 80 | "vulture", 81 | "pdbpp", 82 | "pyright", 83 | "mkdocs", 84 | "mkdocs-material>=9.6.1", 85 | "mkdocstrings-python>=1.13.0", 86 | ] 87 | 88 | [project.urls] 89 | homepage = "https://github.com/edkedk99/streamlit_sql" 90 | documentation = "https://edkedk99.github.io/streamlit_sql/" 91 | repository = "https://github.com/edkedk99/streamlit_sql" 92 | -------------------------------------------------------------------------------- /streamlit_sql/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlit_sql.sql_iu import SqlUi, show_sql_ui 2 | 3 | __all__ = ["SqlUi", "show_sql_ui"] 4 | -------------------------------------------------------------------------------- /streamlit_sql/create_delete_model.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from sqlalchemy import select 3 | from sqlalchemy.orm import DeclarativeBase 4 | from streamlit import session_state as ss 5 | from streamlit.connections.sql_connection import SQLConnection 6 | 7 | from streamlit_sql.filters import ExistingData 8 | from streamlit_sql.input_fields import InputFields 9 | from streamlit_sql.lib import get_pretty_name, log, set_state 10 | 11 | 12 | class CreateRow: 13 | def __init__( 14 | self, 15 | conn: SQLConnection, 16 | Model: type[DeclarativeBase], 17 | default_values: dict | None = None, 18 | base_key: str = "create", 19 | ) -> None: 20 | self.conn = conn 21 | self.Model = Model 22 | 23 | self.default_values = default_values or {} 24 | self.base_key = base_key 25 | 26 | set_state("stsql_updated", 0) 27 | 28 | with conn.session as s: 29 | self.existing_data = ExistingData(s, Model, self.default_values) 30 | self.input_fields = InputFields( 31 | Model, base_key, self.default_values, self.existing_data 32 | ) 33 | 34 | def get_fields(self): 35 | cols = self.Model.__table__.columns 36 | created = {} 37 | for col in cols: 38 | col_name = col.description 39 | assert col_name is not None 40 | default_value = self.default_values.get(col_name) 41 | 42 | if default_value: 43 | input_value = default_value 44 | else: 45 | input_value = self.input_fields.get_input_value(col, None) 46 | 47 | created[col_name] = input_value 48 | 49 | return created 50 | 51 | def show(self, pretty_name: str): 52 | st.subheader(pretty_name) 53 | 54 | with st.form(f"create_model_form_{pretty_name}_{self.base_key}", border=False): 55 | created = self.get_fields() 56 | create_btn = st.form_submit_button("Save", type="primary") 57 | 58 | if create_btn: 59 | row = self.Model(**created) 60 | with self.conn.session as s: 61 | try: 62 | s.add(row) 63 | s.commit() 64 | ss.stsql_updated += 1 65 | log("CREATE", self.Model.__tablename__, row) 66 | return True, f"Criado com sucesso {row}" 67 | except Exception as e: 68 | ss.stsql_updated += 1 69 | log("CREATE", self.Model.__tablename__, row, success=False) 70 | return False, str(e) 71 | else: 72 | return None, None 73 | 74 | def show_dialog(self): 75 | pretty_name = get_pretty_name(self.Model.__tablename__) 76 | 77 | @st.dialog(f"Create {pretty_name}", width="large") # pyright: ignore 78 | def wrap_show_update(): 79 | set_state("stsql_updated", 0) 80 | updated_before = ss.stsql_updated 81 | status, msg = self.show(pretty_name) 82 | 83 | ss.stsql_update_ok = status 84 | ss.stsql_update_message = msg 85 | ss.stsql_opened = True 86 | 87 | if ss.stsql_updated > updated_before: 88 | st.rerun() 89 | 90 | wrap_show_update() 91 | 92 | 93 | class DeleteRows: 94 | def __init__( 95 | self, 96 | conn: SQLConnection, 97 | Model: type[DeclarativeBase], 98 | rows_id: list[int], 99 | base_key: str = "stsql_delete_rows", 100 | ) -> None: 101 | self.conn = conn 102 | self.Model = Model 103 | self.rows_id = rows_id 104 | self.base_key = base_key 105 | 106 | @st.cache_data 107 | def get_rows_str(_self, rows_id: list[int]): 108 | id_col = _self.Model.__table__.columns.get("id") 109 | assert id_col is not None 110 | stmt = select(_self.Model).where(id_col.in_(rows_id)) 111 | 112 | with _self.conn.session as s: 113 | rows = s.execute(stmt).scalars() 114 | rows_str = [str(row) for row in rows] 115 | 116 | return rows_str 117 | 118 | def show(self, pretty_name): 119 | st.subheader("Apagar items abaixo?") 120 | 121 | rows_str = self.get_rows_str(self.rows_id) 122 | st.dataframe({pretty_name: rows_str}, hide_index=True) 123 | 124 | btn = st.button("Delete", key=self.base_key) 125 | if btn: 126 | id_col = self.Model.__table__.columns.get("id") 127 | assert id_col is not None 128 | lancs = [] 129 | with self.conn.session as s: 130 | try: 131 | for row_id in self.rows_id: 132 | lanc = s.get(self.Model, row_id) 133 | lancs.append(str(lanc)) 134 | s.delete(lanc) 135 | 136 | s.commit() 137 | ss.stsql_updated += 1 138 | qtty = len(self.rows_id) 139 | lancs_str = ", ".join(lancs) 140 | log("DELETE", self.Model.__tablename__, lancs_str) 141 | return True, f"Deletado com sucesso {qtty} registros" 142 | except Exception as e: 143 | ss.stsql_updated += 1 144 | log("DELETE", self.Model.__tablename__, "") 145 | return False, str(e) 146 | else: 147 | return None, None 148 | 149 | def show_dialog(self): 150 | pretty_name = get_pretty_name(self.Model.__tablename__) 151 | 152 | @st.dialog(f"Delete {pretty_name}", width="large") # pyright: ignore 153 | def wrap_show_update(): 154 | set_state("stsql_updated", 0) 155 | updated_before = ss.stsql_updated 156 | status, msg = self.show(pretty_name) 157 | 158 | ss.stsql_update_ok = status 159 | ss.stsql_update_message = msg 160 | ss.stsql_opened = True 161 | 162 | if ss.stsql_updated > updated_before: 163 | st.rerun() 164 | 165 | wrap_show_update() 166 | -------------------------------------------------------------------------------- /streamlit_sql/filters.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | from dataclasses import dataclass 3 | from datetime import date 4 | from typing import Any 5 | 6 | import streamlit as st 7 | from dateutil.relativedelta import relativedelta 8 | from sqlalchemy import distinct, func, select 9 | from sqlalchemy.orm import DeclarativeBase 10 | from sqlalchemy.orm.session import Session 11 | from sqlalchemy.sql.schema import ForeignKey 12 | from streamlit import session_state as ss 13 | 14 | 15 | @dataclass 16 | class FkOpt: 17 | idx: int 18 | name: str 19 | 20 | 21 | class ExistingData: 22 | def __init__( 23 | self, 24 | session: Session, 25 | Model: type[DeclarativeBase], 26 | default_values: dict, 27 | row: DeclarativeBase | None = None, 28 | ) -> None: 29 | self.session = session 30 | self.Model = Model 31 | self.default_values = default_values 32 | self.row = row 33 | 34 | self.cols = Model.__table__.columns 35 | reg_values: Any = Model.registry._class_registry.values() 36 | self._models = [reg for reg in reg_values if hasattr(reg, "__tablename__")] 37 | 38 | table_name = Model.__tablename__ 39 | self.text = self.get_text(table_name, ss.stsql_updated) 40 | self.dt = self.get_dt(table_name, ss.stsql_updated) 41 | self.fk = self.get_fk(table_name, ss.stsql_updated) 42 | 43 | def add_default_where(self, stmt, model: type[DeclarativeBase]): 44 | cols = model.__table__.columns 45 | default_values = { 46 | colname: value 47 | for colname, value in self.default_values.items() 48 | if colname in cols 49 | } 50 | 51 | for colname, value in default_values.items(): 52 | default_col = cols.get(colname) 53 | stmt = stmt.where(default_col == value) 54 | 55 | return stmt 56 | 57 | def _get_str_opts(self, column) -> Sequence[str]: 58 | col_name = column.name 59 | stmt = select(distinct(column)).select_from(self.Model).limit(10000) 60 | stmt = self.add_default_where(stmt, self.Model) 61 | 62 | opts = list(self.session.execute(stmt).scalars().all()) 63 | row_value = None 64 | if self.row: 65 | row_value: str | None = getattr(self.row, col_name) 66 | if row_value is not None and row_value not in opts: 67 | opts.append(row_value) 68 | 69 | return opts 70 | 71 | @st.cache_data 72 | def get_text(_self, table_name: str, updated: int) -> dict[str, Sequence[str]]: 73 | opts = { 74 | col.name: _self._get_str_opts(col) 75 | for col in _self.cols 76 | if col.type.python_type is str 77 | } 78 | return opts 79 | 80 | def _get_dt_col(self, column): 81 | min_default = date.today() - relativedelta(days=30) 82 | min_dt: date = self.session.query(func.min(column)).scalar() or min_default 83 | max_dt: date = self.session.query(func.max(column)).scalar() or date.today() 84 | return min_dt, max_dt 85 | 86 | @st.cache_data 87 | def get_dt(_self, table_name: str, updated: int) -> dict[str, tuple[date, date]]: 88 | opts = { 89 | col.name: _self._get_dt_col(col) 90 | for col in _self.cols 91 | if col.type.python_type is date 92 | } 93 | return opts 94 | 95 | def get_foreign_opt(self, row, fk_pk_name: str): 96 | idx = getattr(row, fk_pk_name) 97 | fk_opt = FkOpt(idx, str(row)) 98 | return fk_opt 99 | 100 | def get_foreign_opts(self, col, foreign_key: ForeignKey): 101 | foreign_table_name = foreign_key.column.table.name 102 | model = next( 103 | reg for reg in self._models if reg.__tablename__ == foreign_table_name 104 | ) 105 | fk_pk_name = foreign_key.column.description 106 | stmt = select(model).distinct() 107 | 108 | stmt = self.add_default_where(stmt, model) 109 | 110 | rows = self.session.execute(stmt).scalars() 111 | 112 | opts = [self.get_foreign_opt(row, fk_pk_name) for row in rows] 113 | 114 | opt_row = None 115 | if self.row is not None: 116 | opt_row = self.get_foreign_opt(self.row, fk_pk_name) 117 | if opt_row and opt_row not in opts: 118 | opts.append(opt_row) 119 | 120 | return opts 121 | 122 | @st.cache_data 123 | def get_fk(_self, table_name: str, _updated: int): 124 | fk_cols = [col for col in _self.cols if len(list(col.foreign_keys)) > 0] 125 | opts = { 126 | col.description: _self.get_foreign_opts(col, next(iter(col.foreign_keys))) 127 | for col in fk_cols 128 | if col.description 129 | } 130 | return opts 131 | -------------------------------------------------------------------------------- /streamlit_sql/input_fields.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from decimal import Decimal 3 | 4 | import streamlit as st 5 | from sqlalchemy import Numeric 6 | from sqlalchemy.orm import DeclarativeBase 7 | from sqlalchemy.sql.elements import KeyedColumnElement 8 | from sqlalchemy.types import Enum as SQLEnum 9 | from streamlit_datalist import stDatalist 10 | 11 | from streamlit_sql.filters import ExistingData 12 | from streamlit_sql.lib import get_pretty_name 13 | 14 | 15 | class InputFields: 16 | def __init__( 17 | self, 18 | Model: type[DeclarativeBase], 19 | key_prefix: str, 20 | default_values: dict, 21 | existing_data: ExistingData, 22 | ) -> None: 23 | self.Model = Model 24 | self.key_prefix = key_prefix 25 | self.default_values = default_values 26 | self.existing_data = existing_data 27 | 28 | def input_fk(self, col_name: str, value: int | None): 29 | key = f"{self.key_prefix}_{col_name}" 30 | opts = self.existing_data.fk[col_name] 31 | 32 | index = next((i for i, opt in enumerate(opts) if opt.idx == value), None) 33 | input_value = st.selectbox( 34 | col_name, 35 | options=opts, 36 | format_func=lambda opt: opt.name, 37 | index=index, 38 | key=key, 39 | ) 40 | if not input_value: 41 | return None 42 | return input_value.idx 43 | 44 | def get_col_str_opts(self, col_name: str, value: str | None): 45 | opts = list(self.existing_data.text[col_name]) 46 | if value is None: 47 | return None, opts 48 | 49 | try: 50 | val_index = opts.index(value) 51 | return val_index, opts 52 | except ValueError: 53 | opts.append(value) 54 | val_index = len(opts) - 1 55 | return val_index, opts 56 | 57 | def input_enum(self, col_enum: SQLEnum, col_value=None): 58 | col_name = col_enum.name 59 | assert col_name is not None 60 | opts = col_enum.enums 61 | if col_value: 62 | index = opts.index(col_value) 63 | else: 64 | index = None 65 | input_value = st.selectbox(col_name, opts, index=index) 66 | return input_value 67 | 68 | def input_str(self, col_name: str, value=None): 69 | key = f"{self.key_prefix}_{col_name}" 70 | val_index, opts = self.get_col_str_opts(col_name, value) 71 | input_value = stDatalist( 72 | col_name, 73 | list(opts), 74 | index=val_index, # pyright: ignore 75 | key=key, 76 | ) 77 | result = str(input_value) 78 | return result 79 | 80 | def input_numeric(self, col_name, scale: int | None, value=None): 81 | step = None 82 | if scale: 83 | step = 10 ** (scale * -1) 84 | 85 | value_float = None 86 | if value: 87 | value_float = float(value) 88 | 89 | input_value = st.number_input(col_name, value=value_float, step=step) 90 | 91 | if not input_value: 92 | return None 93 | 94 | value_dec = Decimal(str(input_value)) 95 | if step: 96 | value_dec = value_dec.quantize(Decimal(str(step))) 97 | 98 | return value_dec 99 | 100 | def get_input_value(self, col: KeyedColumnElement, col_value): 101 | col_name = col.description 102 | assert col_name is not None 103 | pretty_name = get_pretty_name(col_name) 104 | 105 | if col.primary_key: 106 | input_value = col_value 107 | elif len(col.foreign_keys) > 0: 108 | input_value = self.input_fk(col_name, col_value) 109 | elif col.type.python_type is str: 110 | input_value = self.input_str(col_name, col_value) 111 | elif col.type.python_type is int: 112 | input_value = st.number_input(pretty_name, value=col_value, step=1) 113 | elif col.type.python_type is float: 114 | input_value = st.number_input(pretty_name, value=col_value, step=0.1) 115 | elif isinstance(col.type, Numeric): 116 | scale = col.type.scale 117 | input_value = self.input_numeric(pretty_name, scale, col_value) 118 | elif col.type.python_type is date: 119 | input_value = st.date_input(pretty_name, value=col_value) 120 | elif col.type.python_type is bool: 121 | input_value = st.checkbox(pretty_name, value=col_value) 122 | elif isinstance(col.type, SQLEnum): 123 | input_value = self.input_enum(col.type, col_value) 124 | else: 125 | input_value = None 126 | 127 | return input_value 128 | -------------------------------------------------------------------------------- /streamlit_sql/lib.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Literal 3 | 4 | import streamlit as st 5 | from loguru import logger 6 | from streamlit import session_state as ss 7 | 8 | 9 | def log( 10 | action: Literal["CREATE", "UPDATE", "DELETE"], 11 | table: str, 12 | row, 13 | success: bool = True, 14 | ): 15 | message = "| Action={} | Table={} | Row={}" 16 | if success: 17 | logger.info(message, action, table, str(row)) 18 | else: 19 | logger.error(message, action, table, str(row)) 20 | 21 | 22 | def set_logging(disable_log: bool): 23 | if disable_log: 24 | logger.disable("streamlit_sql") 25 | return 26 | 27 | logger.enable("streamlit_sql") 28 | if not logger._core.handlers: # pyright: ignore 29 | logger.add(sys.stderr, level="INFO") 30 | 31 | 32 | def set_state(key: str, value): 33 | if key not in ss: 34 | ss[key] = value 35 | 36 | 37 | @st.cache_data 38 | def get_pretty_name(name: str): 39 | pretty_name = " ".join(name.split("_")).title() 40 | return pretty_name 41 | 42 | 43 | if __name__ == "__main__": 44 | set_logging(False) 45 | log(action="CREATE", table="tableA", row="rowabc") 46 | log(action="UPDATE", table="tableB", row="xyzw", success=False) 47 | -------------------------------------------------------------------------------- /streamlit_sql/many.py: -------------------------------------------------------------------------------- 1 | from functools import cached_property 2 | 3 | import pandas as pd 4 | import streamlit as st 5 | from sqlalchemy import func, select 6 | from sqlalchemy.orm import RelationshipProperty, Session 7 | from streamlit.connections.sql_connection import SQLConnection 8 | 9 | from streamlit_sql import lib, read_cte 10 | from streamlit_sql.create_delete_model import CreateRow, DeleteRows 11 | 12 | 13 | class ReadManyRel: 14 | OPTS_ITEMS_PAGE = (50, 100, 200, 500, 1000) 15 | 16 | def __init__( 17 | self, 18 | Model, 19 | model_id: int, 20 | rel: RelationshipProperty, 21 | ) -> None: 22 | self.Model = Model 23 | self.model_id = model_id 24 | self.rel = rel 25 | 26 | @cached_property 27 | def other_col(self): 28 | pairs = self.rel.local_remote_pairs 29 | assert pairs is not None 30 | col = pairs[0][1] 31 | return col 32 | 33 | @cached_property 34 | def other_model(self): 35 | other_col = self.other_col 36 | other_colname: str = other_col.table.name 37 | mappers = self.Model.registry.mappers 38 | 39 | other_model = next( 40 | mapper.class_ 41 | for mapper in mappers 42 | if mapper 43 | if mapper.class_.__tablename__ == other_colname 44 | ) 45 | return other_model 46 | 47 | @cached_property 48 | def suffix_key(self): 49 | model_name: str = self.Model.__table__.name 50 | key = f"{model_name}_{self.other_col.name}_{self.rel.target}" 51 | return key 52 | 53 | @property 54 | def base_stmt(self): 55 | stmt = select(self.other_model.id, self.other_model) 56 | 57 | if self.Model != self.other_model: 58 | stmt = stmt.join(self.Model, self.Model.id == self.other_col) 59 | 60 | stmt = stmt.where(self.other_col == self.model_id) 61 | return stmt 62 | 63 | def get_qtty_rows(self, session: Session): 64 | subq = self.base_stmt.subquery() 65 | stmt = select(func.count(subq.c.id)) 66 | qtty = session.execute(stmt).scalar_one() 67 | return qtty 68 | 69 | def get_stmt_pag(self, items_per_page: int, page: int): 70 | offset = (page - 1) * items_per_page 71 | stmt = self.base_stmt.offset(offset).limit(items_per_page) 72 | return stmt 73 | 74 | def get_data(self, session: Session, items_per_page: int, page: int): 75 | stmt = self.get_stmt_pag(items_per_page, page) 76 | rows = session.execute(stmt) 77 | 78 | result: list[tuple[int, str]] = [(row[0], str(row[1])) for row in rows] 79 | return result 80 | 81 | 82 | @st.fragment 83 | def show_rel(conn: SQLConnection, Model, model_id: int, rel: RelationshipProperty): 84 | read_many_rel = ReadManyRel(Model, model_id, rel) 85 | 86 | exp_name = f"{rel.target} - {read_many_rel.other_col.name}" 87 | pretty_name = lib.get_pretty_name(exp_name) 88 | 89 | with st.expander(pretty_name): 90 | tab_read, tab_create, tab_delete = st.tabs(["Read", "Create", "Delete"]) 91 | data_container = tab_read.container() 92 | pag_container = tab_read.container() 93 | 94 | with pag_container, conn.session as s: 95 | qtty_rows = read_many_rel.get_qtty_rows(s) 96 | items_per_page, page = read_cte.show_pagination( 97 | qtty_rows, 98 | read_many_rel.OPTS_ITEMS_PAGE, 99 | base_key=f"stsql_read_many_pag_{read_many_rel.suffix_key}", 100 | ) 101 | 102 | data = read_many_rel.get_data(s, items_per_page, page) 103 | 104 | with data_container: 105 | df = pd.DataFrame(data, columns=["id", pretty_name]).set_index("id", drop=True) 106 | selection_state = st.dataframe( 107 | df, 108 | hide_index=True, 109 | use_container_width=True, 110 | selection_mode="multi-row", 111 | on_select="rerun", 112 | key=f"stsql_many_df_{read_many_rel.suffix_key}", 113 | ) 114 | 115 | rows_pos = [] 116 | if "selection" in selection_state and "rows" in selection_state["selection"]: 117 | rows_pos = selection_state["selection"]["rows"] 118 | 119 | with tab_create: 120 | default_values = {read_many_rel.other_col.name: model_id} 121 | create_row = CreateRow( 122 | conn, 123 | read_many_rel.other_model, 124 | default_values, 125 | f"stsql_create_many_{read_many_rel.suffix_key}", 126 | ) 127 | create_row.show(pretty_name) 128 | 129 | with tab_delete: 130 | rows_id = df.iloc[rows_pos].index.to_list() 131 | if len(rows_id) == 0: 132 | st.text("Selecione antes na outra aba as linhas para apagar.") 133 | else: 134 | delete_rows = DeleteRows( 135 | conn, 136 | read_many_rel.other_model, 137 | rows_id, 138 | f"st_sql_many_delete_{read_many_rel.suffix_key}", 139 | ) 140 | delete_rows.show(pretty_name) 141 | 142 | 143 | def show_rels(conn: SQLConnection, Model, model_id: int): 144 | rels = [rel for rel in Model.__mapper__.relationships if rel.direction.value == 1] 145 | 146 | for rel in rels: 147 | show_rel(conn, Model, model_id, rel) 148 | -------------------------------------------------------------------------------- /streamlit_sql/params.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | import streamlit as st 4 | from sqlalchemy.sql.elements import KeyedColumnElement 5 | from streamlit import session_state as ss 6 | 7 | from streamlit_sql.filters import FkOpt 8 | 9 | 10 | def get_dt_param(colname: str): 11 | inicio_param = st.query_params.get(f"{colname}_inicio", None) 12 | if inicio_param == "": 13 | inicio = None 14 | else: 15 | inicio = inicio_param 16 | 17 | final_param = st.query_params.get(f"{colname}_final", None) 18 | if final_param == "": 19 | final = None 20 | else: 21 | final = final_param 22 | 23 | return inicio, final 24 | 25 | 26 | def get_no_dt_param(col: KeyedColumnElement, existing: list): 27 | colname = col.description 28 | if not colname: 29 | return None 30 | 31 | param = st.query_params.get(colname, None) 32 | if not param: 33 | return None 34 | 35 | if col.type.python_type is str: 36 | return existing.index(param) 37 | 38 | if col.type.python_type is not int: 39 | return None 40 | 41 | try: 42 | idx = int(param) 43 | except ValueError: 44 | return None 45 | 46 | index = next((i for i, fk in enumerate(existing) if fk.idx == idx), None) 47 | return index 48 | 49 | 50 | def set_dt_param(colname: str, key: str, suffix: str): 51 | query_key = f"{colname}_{suffix}" 52 | value = ss[key] 53 | assert isinstance(value, date) 54 | value_str = value.strftime("%Y-%m-%d") 55 | st.query_params[query_key] = value_str 56 | 57 | 58 | def set_no_dt_param(colname: str, key: str): 59 | value = ss[key] 60 | if isinstance(value, str): 61 | st.query_params[colname] = value 62 | elif isinstance(value, FkOpt): 63 | value_str = str(value.idx) 64 | st.query_params[colname] = value_str 65 | -------------------------------------------------------------------------------- /streamlit_sql/read_cte.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | from datetime import date 3 | from typing import Any 4 | 5 | import pandas as pd 6 | import streamlit as st 7 | import streamlit_antd_components as sac 8 | from sqlalchemy import CTE, Select, distinct, func, select 9 | from sqlalchemy.orm import Session 10 | from sqlalchemy.sql.elements import KeyedColumnElement 11 | from sqlalchemy.types import Enum as SQLEnum 12 | from streamlit.connections.sql_connection import SQLConnection 13 | from streamlit.delta_generator import DeltaGenerator 14 | 15 | from streamlit_sql import params 16 | from streamlit_sql.lib import get_pretty_name 17 | 18 | hash_funcs: dict[Any, Callable[[Any], Any]] = { 19 | pd.Series: lambda serie: serie.to_dict(), 20 | CTE: lambda sel: (str(sel), sel.compile().params), 21 | Select: lambda sel: (str(sel), sel.compile().params), 22 | "streamlit_sql.read_cte.ColFilter": lambda cl: (cl.dt_filters, cl.no_dt_filters), 23 | } 24 | 25 | 26 | def get_existing_cond(col: KeyedColumnElement): 27 | is_str = col.type.python_type is str 28 | is_bool = col.type.python_type is bool 29 | is_enum = isinstance(col.type, SQLEnum) 30 | is_not_pk = not col.primary_key 31 | 32 | fks = list(col.foreign_keys) 33 | has_fk = len(fks) > 0 34 | int_not_fk_cond = col.type.python_type is int and not has_fk 35 | 36 | cond = is_not_pk and (is_str or is_bool or int_not_fk_cond, is_enum) 37 | return cond 38 | 39 | 40 | @st.cache_data(hash_funcs=hash_funcs) 41 | def get_existing_values( 42 | _session: Session, 43 | cte: CTE, 44 | updated: int, 45 | available_col_filter: list[str] | None = None, 46 | ): 47 | if not available_col_filter: 48 | available_col_filter = [] 49 | 50 | cols = list(cte.columns) 51 | 52 | if len(available_col_filter) > 0: 53 | cols = [col for col in cte.columns if get_existing_cond(col)] 54 | 55 | result: dict[str, Any] = {} 56 | for col in cols: 57 | stmt = select(distinct(col)).order_by(col).limit(10000) 58 | values = _session.execute(stmt).scalars().all() 59 | colname = col.description 60 | assert colname is not None 61 | result[colname] = values 62 | 63 | return result 64 | 65 | 66 | class ColFilter: 67 | def __init__( 68 | self, 69 | container: DeltaGenerator, 70 | cte: CTE, 71 | existing_values: dict[str, Any], 72 | available_col_filter: list[str] | None = None, 73 | base_key: str = "", 74 | ) -> None: 75 | self.container = container 76 | self.cte = cte 77 | self.existing_values = existing_values 78 | self.available_col_filter = available_col_filter or [] 79 | self.base_key = base_key 80 | 81 | self.dt_filters = self.get_dt_filters() 82 | self.no_dt_filters = self.get_no_dt_filters() 83 | 84 | def __str__(self): 85 | dt_str = ", ".join( 86 | f"{k}: {dt.strftime('%d/%m/%Y')}" 87 | for k, v in self.dt_filters.items() 88 | for dt in v 89 | if v 90 | if dt 91 | ) 92 | no_dt_str = ", ".join(f"{k}: {v}" for k, v in self.no_dt_filters.items() if v) 93 | 94 | filter_str = "" 95 | if dt_str != "": 96 | filter_str += f"{dt_str}, " 97 | if no_dt_str != "": 98 | filter_str += f"{no_dt_str}" 99 | 100 | return filter_str 101 | 102 | def get_dt_filters(self): 103 | cols = [ 104 | col 105 | for col in self.cte.columns 106 | if col.description in self.available_col_filter 107 | and col.type.python_type is date 108 | ] 109 | 110 | result: dict[str, tuple[date | None, date | None]] = {} 111 | for col in cols: 112 | colname = col.description 113 | assert colname is not None 114 | label = get_pretty_name(colname) 115 | self.container.write(label) 116 | inicio_c, final_c, btn_c = self.container.columns( 117 | [0.475, 0.475, 0.05], vertical_alignment="bottom" 118 | ) 119 | 120 | default_inicio, default_final = params.get_dt_param(colname) 121 | 122 | inicio_key = f"{self.base_key}_date_filter_inicio_{label}" 123 | inicio = inicio_c.date_input( 124 | "Inicio", 125 | value=default_inicio, 126 | key=inicio_key, 127 | args=(colname, inicio_key, "inicio"), 128 | on_change=params.set_dt_param, 129 | ) 130 | 131 | final_key = f"{self.base_key}_date_filter_final_{label}" 132 | final = final_c.date_input( 133 | "Final", 134 | value=default_final, 135 | key=final_key, 136 | args=(colname, final_key, "final"), 137 | on_change=params.set_dt_param, 138 | ) 139 | 140 | btn = btn_c.button( 141 | "", 142 | icon=":material/cancel:", 143 | key=f"st_sql_{inicio_key}_{final_key}_cancel_btn", 144 | ) 145 | if btn: 146 | st.query_params.pop(f"{colname}_inicio", None) 147 | st.query_params.pop(f"{colname}_final", None) 148 | st.rerun() 149 | 150 | assert inicio is None or isinstance(inicio, date) 151 | if inicio is None: 152 | inicio_date = None 153 | else: 154 | inicio_date = date(inicio.year, inicio.month, inicio.day) 155 | 156 | assert final is None or isinstance(final, date) 157 | if final is None: 158 | final_date = None 159 | else: 160 | final_date = date(final.year, final.month, final.day) 161 | 162 | result[colname] = inicio_date, final_date 163 | 164 | return result 165 | 166 | def get_no_dt_filters(self): 167 | cols = [ 168 | col 169 | for col in self.cte.columns 170 | if col.description in self.available_col_filter 171 | and col.type.python_type is not date 172 | ] 173 | 174 | result: dict[str, Any] = {} 175 | for col in cols: 176 | colname = col.description 177 | assert colname is not None 178 | 179 | existing_value = self.existing_values.get(colname) 180 | 181 | if existing_value is None: 182 | return result 183 | 184 | label = get_pretty_name(colname) 185 | key = f"{self.base_key}_no_dt_filter_{label}" 186 | index = params.get_no_dt_param(col, existing_value) 187 | col1, col2 = self.container.columns( 188 | [0.95, 0.05], vertical_alignment="bottom" 189 | ) 190 | value = col1.selectbox( 191 | label, 192 | options=self.existing_values[colname], 193 | index=index, 194 | key=key, 195 | args=(colname, key), 196 | on_change=params.set_no_dt_param, 197 | ) 198 | btn = col2.button(label="", icon=":material/cancel:", key=f"{key}_btn") 199 | if btn: 200 | st.query_params.pop(colname, None) 201 | st.rerun() 202 | 203 | result[colname] = value 204 | 205 | return result 206 | 207 | 208 | def get_stmt_no_pag_dt(cte: CTE, no_dt_filters: dict[str, str | None]): 209 | stmt = select(cte) 210 | for colname, value in no_dt_filters.items(): 211 | if value: 212 | col = cte.columns.get(colname) 213 | assert col is not None 214 | stmt = stmt.where(col == value) 215 | 216 | return stmt 217 | 218 | 219 | def get_stmt_no_pag(cte: CTE, col_filter: ColFilter): 220 | no_dt_filters = col_filter.no_dt_filters 221 | stmt = get_stmt_no_pag_dt(cte, no_dt_filters) 222 | 223 | dt_filters = col_filter.dt_filters 224 | for colname, filters in dt_filters.items(): 225 | col = cte.columns.get(colname) 226 | assert col is not None 227 | inicio, final = filters 228 | if inicio: 229 | stmt = stmt.where(col >= inicio) 230 | if final: 231 | stmt = stmt.where(col <= final) 232 | 233 | return stmt 234 | 235 | 236 | @st.cache_data(hash_funcs=hash_funcs) 237 | def get_qtty_rows(_conn: SQLConnection, stmt_no_pag: Select): 238 | stmt = select(func.count()).select_from(stmt_no_pag.subquery()) 239 | with _conn.session as s: 240 | qtty = s.execute(stmt).scalar_one() 241 | 242 | return qtty 243 | 244 | 245 | def show_pagination(count: int, opts_items_page: tuple[int, ...], base_key: str = ""): 246 | pag_col1, pag_col2 = st.columns([0.2, 0.8]) 247 | 248 | first_item_candidates = [item for item in opts_items_page if item > count] 249 | last_item = ( 250 | first_item_candidates[0] if opts_items_page[-1] > count else opts_items_page[-1] 251 | ) 252 | items_page_str = [str(item) for item in opts_items_page if item <= last_item] 253 | 254 | with pag_col1: 255 | menu_cas = sac.cascader( 256 | items=items_page_str, # pyright: ignore 257 | placeholder="Items per page", 258 | key=f"{base_key}_menu_cascader", 259 | ) 260 | 261 | items_per_page = menu_cas[0] if menu_cas else items_page_str[0] 262 | 263 | with pag_col2: 264 | page = sac.pagination( 265 | total=count, 266 | page_size=int(items_per_page), 267 | show_total=True, 268 | jump=True, 269 | key=f"{base_key}_pagination", 270 | ) 271 | 272 | return (int(items_per_page), int(page)) 273 | 274 | 275 | def get_stmt_pag(stmt_no_pag: Select, limit: int, page: int): 276 | offset = (page - 1) * limit 277 | stmt = stmt_no_pag.offset(offset).limit(limit) 278 | return stmt 279 | 280 | 281 | # @st.cache_data(hash_funcs=hash_funcs) 282 | def initial_balance( 283 | _session: Session, 284 | stmt_no_pag_dt: Select, 285 | stmt_pag: Select, 286 | rolling_total_column: str, 287 | orderby_cols: list, 288 | ) -> float: 289 | stmt_pag_ordered = stmt_pag.order_by(*orderby_cols) 290 | first_pag = _session.execute(stmt_pag_ordered).first() 291 | if not first_pag: 292 | return 0 293 | 294 | stmt_no_pag_dt_ordered = stmt_no_pag_dt.order_by(*orderby_cols) 295 | for col in orderby_cols: 296 | stmt_no_pag_dt_ordered = stmt_no_pag_dt_ordered.where( 297 | col < getattr(first_pag, col.name) 298 | ) 299 | 300 | stmt_bal = select(func.sum(stmt_no_pag_dt_ordered.c.get(rolling_total_column))) 301 | bal = _session.execute(stmt_bal).scalar_one() or 0 302 | return bal 303 | -------------------------------------------------------------------------------- /streamlit_sql/sql_iu.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | 3 | import pandas as pd 4 | import streamlit as st 5 | from sqlalchemy import CTE, Select, select 6 | from sqlalchemy.orm import DeclarativeBase 7 | from sqlalchemy.types import Enum as SQLEnum 8 | from streamlit import session_state as ss 9 | from streamlit.connections import SQLConnection 10 | from streamlit.elements.arrow import DataframeState 11 | 12 | from streamlit_sql import create_delete_model, lib, read_cte, update_model 13 | 14 | OPTS_ITEMS_PAGE = (50, 100, 200, 500, 1000) 15 | 16 | 17 | class SqlUi: 18 | """Show A CRUD interface in a Streamlit Page 19 | 20 | See in __init__ method detailed descriptions of arguments and properties 21 | 22 | It also offers the following properties: 23 | 24 | 25 | """ 26 | 27 | def __init__( 28 | self, 29 | conn: SQLConnection, 30 | read_instance, 31 | edit_create_model: type[DeclarativeBase], 32 | available_filter: list[str] | None = None, 33 | edit_create_default_values: dict | None = None, 34 | rolling_total_column: str | None = None, 35 | rolling_orderby_colsname: list[str] | None = None, 36 | df_style_formatter: dict[str, str] | None = None, 37 | read_use_container_width: bool = False, 38 | hide_id: bool = True, 39 | base_key: str = "", 40 | style_fn: Callable[[pd.Series], list[str]] | None = None, 41 | update_show_many: bool = False, 42 | disable_log: bool = False, 43 | ): 44 | """The CRUD interface will be displayes just by initializing the class 45 | 46 | Arguments: 47 | conn (SQLConnection): A sqlalchemy connection created with st.connection(\"sql\", url=\"\") 48 | read_instance (Select | CTE | Model): The sqlalchemy select statement to display or a CTE. Choose columns to display , join, query or order.If selecting columns, you need to add the id column. If a Model, it will select all columns. 49 | edit_create_default_values (dict, optional): A dict with column name as keys and values to be default. When the user clicks to create a row, those columns will not show on the form and its value will be added to the Model object 50 | available_filter (list[str], optional): Define wich columns the user will be able to filter in the top expander. Defaults to all 51 | rolling_total_column (str, optional): A numeric column name of the read_instance. A new column will be displayed with the rolling sum of these column 52 | rolling_orderby_colsname (list[str], optional): A list of columns name of the read_instance. It should contain a group of columns that ensures uniqueness of the rows and the order to calculate rolling sum. Usually, it should a date and id column. If not informed, rows will be sorted by id only. Defaults to None 53 | df_style_formatter (dict[str,str]): a dictionary where each key is a column name and the associated value is the formatter arg of df.style.format method. See pandas docs for details. 54 | read_use_container_width (bool, optional): add use_container_width to st.dataframe args. Default to False 55 | hide_id (bool, optional): The id column will not be displayed if set to True. Defaults to True 56 | base_key (str, optional): A prefix to add to widget's key argument. This is needed when creating more than one instance of this class in the same page. Defaults to empty str 57 | style_fn (Callable[[pd.Series], list[str]], optional): A function that goes into the *func* argument of *df.style.apply*. The apply method also receives *axis=1*, so it works on rows. It can be used to apply conditional css formatting on each column of the row. See Styler.apply info on pandas docs. Defaults to None 58 | update_show_many (bool, optional): Show a st.expander of one-to-many relations in edit or create dialog 59 | disable_log (bool): Every change in the database (READ, UPDATE, DELETE) is logged to stderr by default. If this is *true*, nothing is logged. To customize the logging format and where it logs to, use loguru as add a new sink to logger. See loguru docs for more information. Dafaults to False 60 | 61 | Attributes: 62 | df (pd.Dataframe): The Dataframe displayed in the screen 63 | selected_rows (list[int]): The position of selected rows. This is not the row id. 64 | qtty_rows (int): The quantity of all rows after filtering 65 | 66 | 67 | Examples: 68 | ```python 69 | def style_fn(row): 70 | if row.amount > 0: 71 | bg = "background-color: rgba(0, 255, 0, 0.1)" 72 | else: 73 | bg = "background-color: rgba(255, 0, 0, 0.2)" 74 | 75 | result = [bg] * len(row) 76 | return result 77 | 78 | 79 | db_url = "sqlite:///data.db" 80 | conn = st.connection("sql", db_url) 81 | 82 | stmt = ( 83 | select( 84 | db.Invoice.id, 85 | db.Invoice.date, 86 | db.Invoice.amount, 87 | db.Client.name, 88 | ) 89 | .join(db.Client) 90 | .where(db.Invoice.amount > 1000) 91 | .order_by(db.Invoice.date) 92 | ) 93 | 94 | sql_ui = SqlUi( 95 | conn=conn, 96 | read_instance=stmt, 97 | edit_create_model=db.Invoice, 98 | available_filter=["name"], 99 | rolling_total_column="amount", 100 | rolling_orderby_colsname=["date", "id"], 101 | df_style_formatter={"amount": "{:,.2f}"}, 102 | read_use_container_width=True, 103 | hide_id=True, 104 | base_key="my_base_sql_ui", 105 | style_fn=style_fn, 106 | update_show_many=True, 107 | disable_log=False, 108 | ) 109 | 110 | ``` 111 | 112 | """ 113 | self.conn = conn 114 | self.read_instance = read_instance 115 | self.edit_create_model = edit_create_model 116 | self.available_filter = available_filter or [] 117 | self.edit_create_default_values = edit_create_default_values or {} 118 | self.rolling_total_column = rolling_total_column 119 | self.rolling_orderby_colsname = rolling_orderby_colsname or ["id"] 120 | self.df_style_formatter = df_style_formatter or {} 121 | self.read_use_container_width = read_use_container_width 122 | self.hide_id = hide_id 123 | self.base_key = base_key 124 | self.style_fn = style_fn 125 | self.update_show_many = update_show_many 126 | self.disable_log = disable_log 127 | 128 | self.cte = self.get_cte() 129 | self.rolling_pretty_name = lib.get_pretty_name(self.rolling_total_column or "") 130 | 131 | # Bootstrap 132 | self.set_initial_state() 133 | self.set_structure() 134 | self.notification() 135 | lib.set_logging(self.disable_log) 136 | 137 | # Create UI 138 | col_filter = self.filter() 139 | stmt_no_pag = read_cte.get_stmt_no_pag(self.cte, col_filter) 140 | qtty_rows = read_cte.get_qtty_rows(self.conn, stmt_no_pag) 141 | items_per_page, page = self.pagination(qtty_rows, col_filter) 142 | stmt_pag = read_cte.get_stmt_pag(stmt_no_pag, items_per_page, page) 143 | initial_balance = self.get_initial_balance( 144 | self.cte, 145 | stmt_pag, 146 | col_filter.no_dt_filters, 147 | rolling_total_column, 148 | self.rolling_orderby_colsname, 149 | ) 150 | df = self.get_df(stmt_pag, initial_balance) 151 | selection_state = self.show_df(df) 152 | rows_selected = self.get_rows_selected(selection_state) 153 | 154 | # CRUD 155 | self.crud(df, rows_selected) 156 | ss.stsql_opened = False 157 | 158 | # Returns 159 | self.df = df 160 | self.rows_selected = rows_selected 161 | self.qtty_rows = qtty_rows 162 | 163 | def set_initial_state(self): 164 | lib.set_state("stsql_updated", 1) 165 | lib.set_state("stsql_update_ok", None) 166 | lib.set_state("stsql_update_message", None) 167 | lib.set_state("stsql_opened", False) 168 | lib.set_state("stsql_filters", {}) 169 | 170 | def set_structure(self): 171 | self.header_container = st.container() 172 | self.data_container = st.container() 173 | self.pag_container = st.container() 174 | 175 | table_name = lib.get_pretty_name(self.edit_create_model.__tablename__) 176 | self.header_container.header(table_name, divider="orange") 177 | 178 | self.expander_container = self.header_container.expander( 179 | "Filter", 180 | icon=":material/search:", 181 | ) 182 | 183 | self.filter_container = self.header_container.container() 184 | 185 | if self.rolling_total_column: 186 | self.saldo_toggle_col, self.saldo_value_col = self.header_container.columns( 187 | 2 188 | ) 189 | 190 | self.btns_container = self.header_container.container() 191 | 192 | def notification(self): 193 | if ss.stsql_update_ok is True: 194 | self.header_container.success( 195 | ss.stsql_update_message, icon=":material/thumb_up:" 196 | ) 197 | if ss.stsql_update_ok is False: 198 | self.header_container.error( 199 | ss.stsql_update_message, icon=":material/thumb_down:" 200 | ) 201 | 202 | def get_cte(self): 203 | if isinstance(self.read_instance, Select): 204 | cte = self.read_instance.cte() 205 | elif isinstance(self.read_instance, CTE): 206 | cte = self.read_instance 207 | else: 208 | cte = select(self.read_instance).cte() 209 | 210 | if self.rolling_total_column: 211 | orderby_cols = [ 212 | cte.columns.get(colname) for colname in self.rolling_orderby_colsname 213 | ] 214 | orderby_cols = [col for col in orderby_cols if col is not None] 215 | cte = select(cte).order_by(*orderby_cols).cte() 216 | 217 | return cte 218 | 219 | def filter(self): 220 | filter_colsname = self.available_filter 221 | if len(filter_colsname) == 0: 222 | filter_colsname = [ 223 | col.description for col in self.cte.columns if col.description 224 | ] 225 | 226 | with self.conn.session as s: 227 | existing = read_cte.get_existing_values( 228 | _session=s, 229 | cte=self.cte, 230 | updated=ss.stsql_updated, 231 | available_col_filter=filter_colsname, 232 | ) 233 | 234 | col_filter = read_cte.ColFilter( 235 | self.expander_container, 236 | self.cte, 237 | existing, 238 | filter_colsname, 239 | self.base_key, 240 | ) 241 | if str(col_filter) != "": 242 | self.filter_container.write(col_filter) 243 | 244 | return col_filter 245 | 246 | def pagination(self, qtty_rows: int, col_filter: read_cte.ColFilter): 247 | with self.pag_container: 248 | items_per_page, page = read_cte.show_pagination( 249 | qtty_rows, 250 | OPTS_ITEMS_PAGE, 251 | self.base_key, 252 | ) 253 | 254 | filters = {**col_filter.no_dt_filters, **col_filter.dt_filters} 255 | if filters != ss.stsql_filters: 256 | page = 1 257 | ss.stsql_filters = filters 258 | 259 | return items_per_page, page 260 | 261 | def get_initial_balance( 262 | self, 263 | base_cte: CTE, 264 | stmt_pag: Select, 265 | no_dt_filters: dict, 266 | rolling_total_column: str | None, 267 | rolling_orderby_colsname: list[str], 268 | ): 269 | if rolling_total_column is None: 270 | return 0 271 | 272 | saldo_toogle = self.saldo_toggle_col.toggle( 273 | f"Adiciona Saldo Anterior em {self.rolling_pretty_name}", 274 | value=True, 275 | key=f"{self.base_key}_saldo_toggle_sql_ui", 276 | ) 277 | 278 | if not saldo_toogle: 279 | return 0 280 | 281 | stmt_no_pag_dt = read_cte.get_stmt_no_pag_dt(base_cte, no_dt_filters) 282 | 283 | orderby_cols = [ 284 | base_cte.columns.get(colname) for colname in rolling_orderby_colsname 285 | ] 286 | orderby_cols = [col for col in orderby_cols if col is not None] 287 | with self.conn.session as s: 288 | initial_balance = read_cte.initial_balance( 289 | _session=s, 290 | stmt_no_pag_dt=stmt_no_pag_dt, 291 | stmt_pag=stmt_pag, 292 | rolling_total_column=rolling_total_column, 293 | orderby_cols=orderby_cols, 294 | ) 295 | 296 | self.saldo_value_col.subheader( 297 | f"Saldo Anterior {self.rolling_pretty_name}: {initial_balance:,.2f}" 298 | ) 299 | 300 | return initial_balance 301 | 302 | def convert_arrow(self, df: pd.DataFrame): 303 | cols = self.cte.columns 304 | for col in cols: 305 | if isinstance(col.type, SQLEnum): 306 | col_name = col.name 307 | df[col_name] = df[col_name].map(lambda v: v.value) 308 | 309 | return df 310 | 311 | def get_df( 312 | self, 313 | stmt_pag: Select, 314 | initial_balance: float, 315 | ): 316 | with self.conn.connect() as c: 317 | df = pd.read_sql(stmt_pag, c) 318 | 319 | df = self.convert_arrow(df) 320 | if self.rolling_total_column is None: 321 | return df 322 | 323 | rolling_col_name = f"Balance {self.rolling_pretty_name}" 324 | df[rolling_col_name] = df[self.rolling_total_column].cumsum() + initial_balance 325 | 326 | return df 327 | 328 | def add_balance_formatter(self, df_style_formatter: dict[str, str]): 329 | formatter = {} 330 | for k, v in df_style_formatter.items(): 331 | formatter[k] = v 332 | if k == self.rolling_total_column: 333 | rolling_col_name = f"Balance {self.rolling_pretty_name}" 334 | formatter[rolling_col_name] = v 335 | 336 | return formatter 337 | 338 | def show_df(self, df: pd.DataFrame): 339 | if df.empty: 340 | st.header(":red[Tabela Vazia]") 341 | return None 342 | 343 | column_order = None 344 | if self.hide_id: 345 | column_order = [colname for colname in df.columns if colname != "id"] 346 | 347 | df_style = df.style 348 | formatter = self.add_balance_formatter(self.df_style_formatter) 349 | df_style = df_style.format(formatter) # pyright: ignore 350 | if self.style_fn is not None: 351 | df_style = df_style.apply(self.style_fn, axis=1) 352 | 353 | selection_state = self.data_container.dataframe( 354 | df_style, 355 | use_container_width=self.read_use_container_width, 356 | height=650, 357 | hide_index=True, 358 | column_order=column_order, 359 | on_select="rerun", 360 | selection_mode="multi-row", 361 | key=f"{self.base_key}_df_sql_ui", 362 | ) 363 | return selection_state 364 | 365 | def get_rows_selected(self, selection_state: DataframeState | None): 366 | rows_pos = [] 367 | if ( 368 | selection_state 369 | and "selection" in selection_state 370 | and "rows" in selection_state["selection"] 371 | ): 372 | rows_pos = selection_state["selection"]["rows"] 373 | 374 | return rows_pos 375 | 376 | def crud(self, df: pd.DataFrame, rows_selected: list[int]): 377 | qtty_rows = len(rows_selected) 378 | action = update_model.action_btns( 379 | self.btns_container, 380 | qtty_rows, 381 | ss.stsql_opened, 382 | ) 383 | 384 | if action == "add": 385 | create_row = create_delete_model.CreateRow( 386 | conn=self.conn, 387 | Model=self.edit_create_model, 388 | default_values=self.edit_create_default_values, 389 | ) 390 | create_row.show_dialog() 391 | elif action == "edit": 392 | selected_pos = rows_selected[0] 393 | row_id = int(df.iloc[selected_pos]["id"]) 394 | update_row = update_model.UpdateRow( 395 | conn=self.conn, 396 | Model=self.edit_create_model, 397 | row_id=row_id, 398 | default_values=self.edit_create_default_values, 399 | update_show_many=self.update_show_many, 400 | ) 401 | update_row.show_dialog() 402 | elif action == "delete": 403 | rows_id = df.iloc[rows_selected].id.astype(int).to_list() 404 | delete_rows = create_delete_model.DeleteRows( 405 | conn=self.conn, 406 | Model=self.edit_create_model, 407 | rows_id=rows_id, 408 | ) 409 | delete_rows.show_dialog() 410 | 411 | 412 | def show_sql_ui( 413 | conn: SQLConnection, 414 | read_instance, 415 | edit_create_model: type[DeclarativeBase], 416 | available_filter: list[str] | None = None, 417 | edit_create_default_values: dict | None = None, 418 | rolling_total_column: str | None = None, 419 | rolling_orderby_colsname: list[str] | None = None, 420 | df_style_formatter: dict[str, str] | None = None, 421 | read_use_container_width: bool = False, 422 | hide_id: bool = True, 423 | base_key: str = "", 424 | style_fn: Callable[[pd.Series], list[str]] | None = None, 425 | update_show_many: bool = False, 426 | ) -> tuple[pd.DataFrame, list[int]] | None: 427 | """Show A CRUD interface in a Streamlit Page 428 | 429 | This function is deprecated and will be removed in future versions. See SqlUi class docs for details on each argument. 430 | 431 | Returns: 432 | tuple[pd.DataFrame, list[int]]: A Tuple with the DataFrame displayed as first item and a list of rows numbers selected as second item. 433 | 434 | Example: 435 | See SqlUi class for an example. 436 | 437 | """ 438 | ui = SqlUi( 439 | conn=conn, 440 | read_instance=read_instance, 441 | edit_create_model=edit_create_model, 442 | available_filter=available_filter, 443 | edit_create_default_values=edit_create_default_values, 444 | rolling_total_column=rolling_total_column, 445 | rolling_orderby_colsname=rolling_orderby_colsname, 446 | df_style_formatter=df_style_formatter, 447 | read_use_container_width=read_use_container_width, 448 | hide_id=hide_id, 449 | base_key=base_key, 450 | style_fn=style_fn, 451 | update_show_many=update_show_many, 452 | ) 453 | 454 | return ui.df, ui.rows_selected 455 | -------------------------------------------------------------------------------- /streamlit_sql/update_model.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from sqlalchemy import select 3 | from sqlalchemy.orm import DeclarativeBase 4 | from streamlit import session_state as ss 5 | from streamlit.connections.sql_connection import SQLConnection 6 | from streamlit.delta_generator import DeltaGenerator 7 | 8 | from streamlit_sql import many 9 | from streamlit_sql.filters import ExistingData 10 | from streamlit_sql.input_fields import InputFields 11 | from streamlit_sql.lib import get_pretty_name, log, set_state 12 | 13 | 14 | class UpdateRow: 15 | def __init__( 16 | self, 17 | conn: SQLConnection, 18 | Model: type[DeclarativeBase], 19 | row_id: int, 20 | default_values: dict | None = None, 21 | update_show_many: bool = False, 22 | ) -> None: 23 | self.conn = conn 24 | self.Model = Model 25 | self.row_id = row_id 26 | self.default_values = default_values or {} 27 | self.update_show_many = update_show_many 28 | 29 | set_state("stsql_updated", 0) 30 | 31 | with conn.session as s: 32 | self.row = s.get_one(Model, row_id) 33 | self.existing_data = ExistingData(s, Model, self.default_values, self.row) 34 | 35 | self.input_fields = InputFields( 36 | Model, "update", self.default_values, self.existing_data 37 | ) 38 | 39 | def get_updates(self): 40 | cols = self.Model.__table__.columns 41 | updated = {} 42 | 43 | for col in cols: 44 | col_name = col.description 45 | assert col_name is not None 46 | col_value = getattr(self.row, col_name) 47 | default_value = self.default_values.get(col_name) 48 | 49 | if default_value: 50 | input_value = col_value 51 | else: 52 | input_value = self.input_fields.get_input_value(col, col_value) 53 | 54 | updated[col_name] = input_value 55 | 56 | return updated 57 | 58 | def save(self, updated: dict): 59 | with self.conn.session as s: 60 | try: 61 | stmt = select(self.Model).where( 62 | self.Model.__table__.columns.id == updated["id"] 63 | ) 64 | row = s.execute(stmt).scalar_one() 65 | for k, v in updated.items(): 66 | setattr(row, k, v) 67 | 68 | s.add(row) 69 | s.commit() 70 | log("UPDATE", self.Model.__tablename__, row) 71 | return True, f"Atualizado com sucesso {row}" 72 | except Exception as e: 73 | updated_list = [f"{k}: {v}" for k, v in updated.items()] 74 | updated_str = ", ".join(updated_list) 75 | log("UPDATE", self.Model.__tablename__, updated_str) 76 | return False, str(e) 77 | 78 | def show(self): 79 | pretty_name = get_pretty_name(self.Model.__tablename__) 80 | st.subheader(pretty_name) 81 | with st.form(f"update_model_form_{pretty_name}", border=False): 82 | updated = self.get_updates() 83 | update_btn = st.form_submit_button("Save") 84 | 85 | if self.update_show_many: 86 | many.show_rels(self.conn, self.Model, self.row_id) 87 | 88 | if update_btn: 89 | ss.stsql_updated += 1 90 | return self.save(updated) 91 | return None, None 92 | 93 | def show_dialog(self): 94 | pretty_name = get_pretty_name(self.Model.__tablename__) 95 | 96 | @st.dialog(f"Edit {pretty_name}", width="large") # pyright: ignore 97 | def wrap_show_update(): 98 | set_state("stsql_updated", 0) 99 | updated_before = ss.stsql_updated 100 | status, msg = self.show() 101 | 102 | ss.stsql_update_ok = status 103 | ss.stsql_update_message = msg 104 | ss.stsql_opened = True 105 | 106 | if ss.stsql_updated > updated_before: 107 | st.rerun() 108 | 109 | wrap_show_update() 110 | 111 | 112 | def action_btns(container: DeltaGenerator, qtty_selected: int, opened: bool): 113 | set_state("stsql_action", "") 114 | disabled_add = qtty_selected > 0 115 | disabled_edit = qtty_selected != 1 116 | disabled_delete = qtty_selected == 0 117 | 118 | with container: 119 | add_col, edit_col, del_col, _empty_col = st.columns([1, 1, 1, 6]) 120 | 121 | add_btn = add_col.button( 122 | "", 123 | help="Add", 124 | icon=":material/add:", 125 | type="secondary", 126 | disabled=disabled_add, 127 | use_container_width=True, 128 | ) 129 | 130 | edit_btn = edit_col.button( 131 | "", 132 | help="Edit", 133 | icon=":material/edit:", 134 | type="secondary", 135 | disabled=disabled_edit, 136 | use_container_width=True, 137 | ) 138 | 139 | del_btn = del_col.button( 140 | "", 141 | help="Delete", 142 | icon=":material/delete:", 143 | type="primary", 144 | disabled=disabled_delete, 145 | use_container_width=True, 146 | ) 147 | 148 | if opened: 149 | return None 150 | if add_btn: 151 | return "add" 152 | if edit_btn: 153 | return "edit" 154 | if del_btn: 155 | return "delete" 156 | 157 | return None 158 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.13" 3 | 4 | [[package]] 5 | name = "altair" 6 | version = "5.5.0" 7 | source = { registry = "https://pypi.org/simple" } 8 | dependencies = [ 9 | { name = "jinja2" }, 10 | { name = "jsonschema" }, 11 | { name = "narwhals" }, 12 | { name = "packaging" }, 13 | { name = "typing-extensions", marker = "python_full_version < '3.14'" }, 14 | ] 15 | sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305 } 16 | wheels = [ 17 | { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200 }, 18 | ] 19 | 20 | [[package]] 21 | name = "asttokens" 22 | version = "3.0.0" 23 | source = { registry = "https://pypi.org/simple" } 24 | sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } 25 | wheels = [ 26 | { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, 27 | ] 28 | 29 | [[package]] 30 | name = "attrs" 31 | version = "25.1.0" 32 | source = { registry = "https://pypi.org/simple" } 33 | sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } 34 | wheels = [ 35 | { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, 36 | ] 37 | 38 | [[package]] 39 | name = "babel" 40 | version = "2.16.0" 41 | source = { registry = "https://pypi.org/simple" } 42 | sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } 43 | wheels = [ 44 | { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, 45 | ] 46 | 47 | [[package]] 48 | name = "blinker" 49 | version = "1.9.0" 50 | source = { registry = "https://pypi.org/simple" } 51 | sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } 52 | wheels = [ 53 | { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, 54 | ] 55 | 56 | [[package]] 57 | name = "build" 58 | version = "1.2.2.post1" 59 | source = { registry = "https://pypi.org/simple" } 60 | dependencies = [ 61 | { name = "colorama", marker = "os_name == 'nt'" }, 62 | { name = "packaging" }, 63 | { name = "pyproject-hooks" }, 64 | ] 65 | sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701 } 66 | wheels = [ 67 | { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950 }, 68 | ] 69 | 70 | [[package]] 71 | name = "cachetools" 72 | version = "5.5.1" 73 | source = { registry = "https://pypi.org/simple" } 74 | sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 } 75 | wheels = [ 76 | { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 }, 77 | ] 78 | 79 | [[package]] 80 | name = "certifi" 81 | version = "2025.1.31" 82 | source = { registry = "https://pypi.org/simple" } 83 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 84 | wheels = [ 85 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 86 | ] 87 | 88 | [[package]] 89 | name = "charset-normalizer" 90 | version = "3.4.1" 91 | source = { registry = "https://pypi.org/simple" } 92 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } 93 | wheels = [ 94 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, 95 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, 96 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, 97 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, 98 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, 99 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, 100 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, 101 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, 102 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, 103 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, 104 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, 105 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, 106 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, 107 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, 108 | ] 109 | 110 | [[package]] 111 | name = "click" 112 | version = "8.1.8" 113 | source = { registry = "https://pypi.org/simple" } 114 | dependencies = [ 115 | { name = "colorama", marker = "sys_platform == 'win32'" }, 116 | ] 117 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 118 | wheels = [ 119 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 120 | ] 121 | 122 | [[package]] 123 | name = "colorama" 124 | version = "0.4.6" 125 | source = { registry = "https://pypi.org/simple" } 126 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 127 | wheels = [ 128 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 129 | ] 130 | 131 | [[package]] 132 | name = "decorator" 133 | version = "5.1.1" 134 | source = { registry = "https://pypi.org/simple" } 135 | sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } 136 | wheels = [ 137 | { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, 138 | ] 139 | 140 | [[package]] 141 | name = "executing" 142 | version = "2.2.0" 143 | source = { registry = "https://pypi.org/simple" } 144 | sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } 145 | wheels = [ 146 | { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, 147 | ] 148 | 149 | [[package]] 150 | name = "fancycompleter" 151 | version = "0.9.1" 152 | source = { registry = "https://pypi.org/simple" } 153 | dependencies = [ 154 | { name = "pyreadline", marker = "sys_platform == 'win32'" }, 155 | { name = "pyrepl" }, 156 | ] 157 | sdist = { url = "https://files.pythonhosted.org/packages/a9/95/649d135442d8ecf8af5c7e235550c628056423c96c4bc6787348bdae9248/fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272", size = 10866 } 158 | wheels = [ 159 | { url = "https://files.pythonhosted.org/packages/38/ef/c08926112034d017633f693d3afc8343393a035134a29dfc12dcd71b0375/fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080", size = 9681 }, 160 | ] 161 | 162 | [[package]] 163 | name = "ghp-import" 164 | version = "2.1.0" 165 | source = { registry = "https://pypi.org/simple" } 166 | dependencies = [ 167 | { name = "python-dateutil" }, 168 | ] 169 | sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } 170 | wheels = [ 171 | { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, 172 | ] 173 | 174 | [[package]] 175 | name = "gitdb" 176 | version = "4.0.12" 177 | source = { registry = "https://pypi.org/simple" } 178 | dependencies = [ 179 | { name = "smmap" }, 180 | ] 181 | sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } 182 | wheels = [ 183 | { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, 184 | ] 185 | 186 | [[package]] 187 | name = "gitpython" 188 | version = "3.1.44" 189 | source = { registry = "https://pypi.org/simple" } 190 | dependencies = [ 191 | { name = "gitdb" }, 192 | ] 193 | sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } 194 | wheels = [ 195 | { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, 196 | ] 197 | 198 | [[package]] 199 | name = "greenlet" 200 | version = "3.1.1" 201 | source = { registry = "https://pypi.org/simple" } 202 | sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } 203 | wheels = [ 204 | { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, 205 | { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, 206 | { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, 207 | { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, 208 | { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, 209 | { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, 210 | { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, 211 | { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, 212 | { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, 213 | { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, 214 | { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, 215 | { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, 216 | { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, 217 | { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, 218 | { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, 219 | { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, 220 | ] 221 | 222 | [[package]] 223 | name = "griffe" 224 | version = "1.5.6" 225 | source = { registry = "https://pypi.org/simple" } 226 | dependencies = [ 227 | { name = "colorama" }, 228 | ] 229 | sdist = { url = "https://files.pythonhosted.org/packages/88/f0/a001e06c321dfa220103418259afbac50b933eac7a86657a4b572f0517e8/griffe-1.5.6.tar.gz", hash = "sha256:181f6666d5aceb6cd6e2da5a2b646cfb431e47a0da1fda283845734b67e10944", size = 391173 } 230 | wheels = [ 231 | { url = "https://files.pythonhosted.org/packages/b6/87/505777c4e5ca9c4fa5ae53fa4b0d5c2ba13a6d55a503a5594e94a2ba9b5a/griffe-1.5.6-py3-none-any.whl", hash = "sha256:b2a3afe497c6c1f952e54a23095ecc09435016293e77af8478ed65df1022a394", size = 128176 }, 232 | ] 233 | 234 | [[package]] 235 | name = "idna" 236 | version = "3.10" 237 | source = { registry = "https://pypi.org/simple" } 238 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 239 | wheels = [ 240 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 241 | ] 242 | 243 | [[package]] 244 | name = "ipython" 245 | version = "8.32.0" 246 | source = { registry = "https://pypi.org/simple" } 247 | dependencies = [ 248 | { name = "colorama", marker = "sys_platform == 'win32'" }, 249 | { name = "decorator" }, 250 | { name = "jedi" }, 251 | { name = "matplotlib-inline" }, 252 | { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, 253 | { name = "prompt-toolkit" }, 254 | { name = "pygments" }, 255 | { name = "stack-data" }, 256 | { name = "traitlets" }, 257 | ] 258 | sdist = { url = "https://files.pythonhosted.org/packages/36/80/4d2a072e0db7d250f134bc11676517299264ebe16d62a8619d49a78ced73/ipython-8.32.0.tar.gz", hash = "sha256:be2c91895b0b9ea7ba49d33b23e2040c352b33eb6a519cca7ce6e0c743444251", size = 5507441 } 259 | wheels = [ 260 | { url = "https://files.pythonhosted.org/packages/e7/e1/f4474a7ecdb7745a820f6f6039dc43c66add40f1bcc66485607d93571af6/ipython-8.32.0-py3-none-any.whl", hash = "sha256:cae85b0c61eff1fc48b0a8002de5958b6528fa9c8defb1894da63f42613708aa", size = 825524 }, 261 | ] 262 | 263 | [[package]] 264 | name = "isort" 265 | version = "6.0.0" 266 | source = { registry = "https://pypi.org/simple" } 267 | sdist = { url = "https://files.pythonhosted.org/packages/1c/28/b382d1656ac0ee4cef4bf579b13f9c6c813bff8a5cb5996669592c8c75fa/isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1", size = 828356 } 268 | wheels = [ 269 | { url = "https://files.pythonhosted.org/packages/76/c7/d6017f09ae5b1206fbe531f7af3b6dac1f67aedcbd2e79f3b386c27955d6/isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892", size = 94053 }, 270 | ] 271 | 272 | [[package]] 273 | name = "jedi" 274 | version = "0.19.2" 275 | source = { registry = "https://pypi.org/simple" } 276 | dependencies = [ 277 | { name = "parso" }, 278 | ] 279 | sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } 280 | wheels = [ 281 | { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, 282 | ] 283 | 284 | [[package]] 285 | name = "jinja2" 286 | version = "3.1.5" 287 | source = { registry = "https://pypi.org/simple" } 288 | dependencies = [ 289 | { name = "markupsafe" }, 290 | ] 291 | sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } 292 | wheels = [ 293 | { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, 294 | ] 295 | 296 | [[package]] 297 | name = "jsonschema" 298 | version = "4.23.0" 299 | source = { registry = "https://pypi.org/simple" } 300 | dependencies = [ 301 | { name = "attrs" }, 302 | { name = "jsonschema-specifications" }, 303 | { name = "referencing" }, 304 | { name = "rpds-py" }, 305 | ] 306 | sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } 307 | wheels = [ 308 | { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, 309 | ] 310 | 311 | [[package]] 312 | name = "jsonschema-specifications" 313 | version = "2024.10.1" 314 | source = { registry = "https://pypi.org/simple" } 315 | dependencies = [ 316 | { name = "referencing" }, 317 | ] 318 | sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } 319 | wheels = [ 320 | { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, 321 | ] 322 | 323 | [[package]] 324 | name = "loguru" 325 | version = "0.7.3" 326 | source = { registry = "https://pypi.org/simple" } 327 | dependencies = [ 328 | { name = "colorama", marker = "sys_platform == 'win32'" }, 329 | { name = "win32-setctime", marker = "sys_platform == 'win32'" }, 330 | ] 331 | sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 } 332 | wheels = [ 333 | { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 }, 334 | ] 335 | 336 | [[package]] 337 | name = "markdown" 338 | version = "3.7" 339 | source = { registry = "https://pypi.org/simple" } 340 | sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } 341 | wheels = [ 342 | { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, 343 | ] 344 | 345 | [[package]] 346 | name = "markdown-it-py" 347 | version = "3.0.0" 348 | source = { registry = "https://pypi.org/simple" } 349 | dependencies = [ 350 | { name = "mdurl" }, 351 | ] 352 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 353 | wheels = [ 354 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 355 | ] 356 | 357 | [[package]] 358 | name = "markupsafe" 359 | version = "3.0.2" 360 | source = { registry = "https://pypi.org/simple" } 361 | sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } 362 | wheels = [ 363 | { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, 364 | { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, 365 | { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, 366 | { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, 367 | { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, 368 | { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, 369 | { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, 370 | { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, 371 | { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, 372 | { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, 373 | { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, 374 | { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, 375 | { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, 376 | { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, 377 | { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, 378 | { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, 379 | { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, 380 | { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, 381 | { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, 382 | { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, 383 | ] 384 | 385 | [[package]] 386 | name = "matplotlib-inline" 387 | version = "0.1.7" 388 | source = { registry = "https://pypi.org/simple" } 389 | dependencies = [ 390 | { name = "traitlets" }, 391 | ] 392 | sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } 393 | wheels = [ 394 | { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, 395 | ] 396 | 397 | [[package]] 398 | name = "mdurl" 399 | version = "0.1.2" 400 | source = { registry = "https://pypi.org/simple" } 401 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 402 | wheels = [ 403 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 404 | ] 405 | 406 | [[package]] 407 | name = "mergedeep" 408 | version = "1.3.4" 409 | source = { registry = "https://pypi.org/simple" } 410 | sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } 411 | wheels = [ 412 | { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, 413 | ] 414 | 415 | [[package]] 416 | name = "mkdocs" 417 | version = "1.6.1" 418 | source = { registry = "https://pypi.org/simple" } 419 | dependencies = [ 420 | { name = "click" }, 421 | { name = "colorama", marker = "sys_platform == 'win32'" }, 422 | { name = "ghp-import" }, 423 | { name = "jinja2" }, 424 | { name = "markdown" }, 425 | { name = "markupsafe" }, 426 | { name = "mergedeep" }, 427 | { name = "mkdocs-get-deps" }, 428 | { name = "packaging" }, 429 | { name = "pathspec" }, 430 | { name = "pyyaml" }, 431 | { name = "pyyaml-env-tag" }, 432 | { name = "watchdog" }, 433 | ] 434 | sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } 435 | wheels = [ 436 | { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, 437 | ] 438 | 439 | [[package]] 440 | name = "mkdocs-autorefs" 441 | version = "1.3.0" 442 | source = { registry = "https://pypi.org/simple" } 443 | dependencies = [ 444 | { name = "markdown" }, 445 | { name = "markupsafe" }, 446 | { name = "mkdocs" }, 447 | ] 448 | sdist = { url = "https://files.pythonhosted.org/packages/fe/18/fb1e17fb705228b51bf7b2f791adaf83c0fa708e51bbc003411ba48ae21e/mkdocs_autorefs-1.3.0.tar.gz", hash = "sha256:6867764c099ace9025d6ac24fd07b85a98335fbd30107ef01053697c8f46db61", size = 42597 } 449 | wheels = [ 450 | { url = "https://files.pythonhosted.org/packages/f4/4a/960c441950f98becfa5dd419adab20274939fd575ab848aee2c87e3599ac/mkdocs_autorefs-1.3.0-py3-none-any.whl", hash = "sha256:d180f9778a04e78b7134e31418f238bba56f56d6a8af97873946ff661befffb3", size = 17642 }, 451 | ] 452 | 453 | [[package]] 454 | name = "mkdocs-get-deps" 455 | version = "0.2.0" 456 | source = { registry = "https://pypi.org/simple" } 457 | dependencies = [ 458 | { name = "mergedeep" }, 459 | { name = "platformdirs" }, 460 | { name = "pyyaml" }, 461 | ] 462 | sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } 463 | wheels = [ 464 | { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, 465 | ] 466 | 467 | [[package]] 468 | name = "mkdocs-material" 469 | version = "9.6.1" 470 | source = { registry = "https://pypi.org/simple" } 471 | dependencies = [ 472 | { name = "babel" }, 473 | { name = "colorama" }, 474 | { name = "jinja2" }, 475 | { name = "markdown" }, 476 | { name = "mkdocs" }, 477 | { name = "mkdocs-material-extensions" }, 478 | { name = "paginate" }, 479 | { name = "pygments" }, 480 | { name = "pymdown-extensions" }, 481 | { name = "regex" }, 482 | { name = "requests" }, 483 | ] 484 | sdist = { url = "https://files.pythonhosted.org/packages/86/48/0b898b78d5be42a453953aa899cd5afb6f042f31c0780ef3316451e29ed4/mkdocs_material-9.6.1.tar.gz", hash = "sha256:da37dba220d9fbfc5f1fc567fafc4028e3c3d7d828f2779ed09ab726ceca77dc", size = 3942506 } 485 | wheels = [ 486 | { url = "https://files.pythonhosted.org/packages/c3/99/82699a2701bd0b1f99cbb7f77af1443f454552407fa2e95530c8537dccb7/mkdocs_material-9.6.1-py3-none-any.whl", hash = "sha256:c1742d410be29811a9b7e863cb25a578b9e255fe6f04c69f8c6838863a58e141", size = 8688576 }, 487 | ] 488 | 489 | [[package]] 490 | name = "mkdocs-material-extensions" 491 | version = "1.3.1" 492 | source = { registry = "https://pypi.org/simple" } 493 | sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } 494 | wheels = [ 495 | { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, 496 | ] 497 | 498 | [[package]] 499 | name = "mkdocstrings" 500 | version = "0.27.0" 501 | source = { registry = "https://pypi.org/simple" } 502 | dependencies = [ 503 | { name = "click" }, 504 | { name = "jinja2" }, 505 | { name = "markdown" }, 506 | { name = "markupsafe" }, 507 | { name = "mkdocs" }, 508 | { name = "mkdocs-autorefs" }, 509 | { name = "platformdirs" }, 510 | { name = "pymdown-extensions" }, 511 | ] 512 | sdist = { url = "https://files.pythonhosted.org/packages/e2/5a/5de70538c2cefae7ac3a15b5601e306ef3717290cb2aab11d51cbbc2d1c0/mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657", size = 94830 } 513 | wheels = [ 514 | { url = "https://files.pythonhosted.org/packages/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332", size = 30658 }, 515 | ] 516 | 517 | [[package]] 518 | name = "mkdocstrings-python" 519 | version = "1.13.0" 520 | source = { registry = "https://pypi.org/simple" } 521 | dependencies = [ 522 | { name = "griffe" }, 523 | { name = "mkdocs-autorefs" }, 524 | { name = "mkdocstrings" }, 525 | ] 526 | sdist = { url = "https://files.pythonhosted.org/packages/ab/ae/32703e35d74040051c672400fd9f5f2b48a6ea094f5071dd8a0e3be35322/mkdocstrings_python-1.13.0.tar.gz", hash = "sha256:2dbd5757e8375b9720e81db16f52f1856bf59905428fd7ef88005d1370e2f64c", size = 185697 } 527 | wheels = [ 528 | { url = "https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl", hash = "sha256:b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97", size = 112254 }, 529 | ] 530 | 531 | [[package]] 532 | name = "narwhals" 533 | version = "1.24.2" 534 | source = { registry = "https://pypi.org/simple" } 535 | sdist = { url = "https://files.pythonhosted.org/packages/8f/db/f5d3b2d2f759ac341f8ce909e8df5233945768344c44d0cec007bce41de6/narwhals-1.24.2.tar.gz", hash = "sha256:c7bbb96ec8efb22ee384872e4a57cff591df653dc49067c314f88e2a54d8a76b", size = 254303 } 536 | wheels = [ 537 | { url = "https://files.pythonhosted.org/packages/f9/cf/6723d679c1eb75f9b976ed98bb3575c6bb571ffd427479288b98819604dd/narwhals-1.24.2-py3-none-any.whl", hash = "sha256:5d70dda4c032c86d0bda3f4d60a9d3f8dbd7d83deb8d23d68dd2f2eb3bc86417", size = 312773 }, 538 | ] 539 | 540 | [[package]] 541 | name = "nodeenv" 542 | version = "1.9.1" 543 | source = { registry = "https://pypi.org/simple" } 544 | sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } 545 | wheels = [ 546 | { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, 547 | ] 548 | 549 | [[package]] 550 | name = "numpy" 551 | version = "2.2.2" 552 | source = { registry = "https://pypi.org/simple" } 553 | sdist = { url = "https://files.pythonhosted.org/packages/ec/d0/c12ddfd3a02274be06ffc71f3efc6d0e457b0409c4481596881e748cb264/numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f", size = 20233295 } 554 | wheels = [ 555 | { url = "https://files.pythonhosted.org/packages/e1/fe/df5624001f4f5c3e0b78e9017bfab7fdc18a8d3b3d3161da3d64924dd659/numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", size = 20899188 }, 556 | { url = "https://files.pythonhosted.org/packages/a9/80/d349c3b5ed66bd3cb0214be60c27e32b90a506946857b866838adbe84040/numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", size = 14113972 }, 557 | { url = "https://files.pythonhosted.org/packages/9d/50/949ec9cbb28c4b751edfa64503f0913cbfa8d795b4a251e7980f13a8a655/numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", size = 5114294 }, 558 | { url = "https://files.pythonhosted.org/packages/8d/f3/399c15629d5a0c68ef2aa7621d430b2be22034f01dd7f3c65a9c9666c445/numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", size = 6648426 }, 559 | { url = "https://files.pythonhosted.org/packages/2c/03/c72474c13772e30e1bc2e558cdffd9123c7872b731263d5648b5c49dd459/numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", size = 14045990 }, 560 | { url = "https://files.pythonhosted.org/packages/83/9c/96a9ab62274ffafb023f8ee08c88d3d31ee74ca58869f859db6845494fa6/numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", size = 16096614 }, 561 | { url = "https://files.pythonhosted.org/packages/d5/34/cd0a735534c29bec7093544b3a509febc9b0df77718a9b41ffb0809c9f46/numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", size = 15242123 }, 562 | { url = "https://files.pythonhosted.org/packages/5e/6d/541717a554a8f56fa75e91886d9b79ade2e595918690eb5d0d3dbd3accb9/numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", size = 17859160 }, 563 | { url = "https://files.pythonhosted.org/packages/b9/a5/fbf1f2b54adab31510728edd06a05c1b30839f37cf8c9747cb85831aaf1b/numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", size = 6273337 }, 564 | { url = "https://files.pythonhosted.org/packages/56/e5/01106b9291ef1d680f82bc47d0c5b5e26dfed15b0754928e8f856c82c881/numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", size = 12609010 }, 565 | { url = "https://files.pythonhosted.org/packages/9f/30/f23d9876de0f08dceb707c4dcf7f8dd7588266745029debb12a3cdd40be6/numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", size = 20924451 }, 566 | { url = "https://files.pythonhosted.org/packages/6a/ec/6ea85b2da9d5dfa1dbb4cb3c76587fc8ddcae580cb1262303ab21c0926c4/numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", size = 14122390 }, 567 | { url = "https://files.pythonhosted.org/packages/68/05/bfbdf490414a7dbaf65b10c78bc243f312c4553234b6d91c94eb7c4b53c2/numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", size = 5156590 }, 568 | { url = "https://files.pythonhosted.org/packages/f7/ec/fe2e91b2642b9d6544518388a441bcd65c904cea38d9ff998e2e8ebf808e/numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", size = 6671958 }, 569 | { url = "https://files.pythonhosted.org/packages/b1/6f/6531a78e182f194d33ee17e59d67d03d0d5a1ce7f6be7343787828d1bd4a/numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", size = 14019950 }, 570 | { url = "https://files.pythonhosted.org/packages/e1/fb/13c58591d0b6294a08cc40fcc6b9552d239d773d520858ae27f39997f2ae/numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", size = 16079759 }, 571 | { url = "https://files.pythonhosted.org/packages/2c/f2/f2f8edd62abb4b289f65a7f6d1f3650273af00b91b7267a2431be7f1aec6/numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", size = 15226139 }, 572 | { url = "https://files.pythonhosted.org/packages/aa/29/14a177f1a90b8ad8a592ca32124ac06af5eff32889874e53a308f850290f/numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", size = 17856316 }, 573 | { url = "https://files.pythonhosted.org/packages/95/03/242ae8d7b97f4e0e4ab8dd51231465fb23ed5e802680d629149722e3faf1/numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", size = 6329134 }, 574 | { url = "https://files.pythonhosted.org/packages/80/94/cd9e9b04012c015cb6320ab3bf43bc615e248dddfeb163728e800a5d96f0/numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", size = 12696208 }, 575 | ] 576 | 577 | [[package]] 578 | name = "packaging" 579 | version = "24.2" 580 | source = { registry = "https://pypi.org/simple" } 581 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 582 | wheels = [ 583 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 584 | ] 585 | 586 | [[package]] 587 | name = "paginate" 588 | version = "0.5.7" 589 | source = { registry = "https://pypi.org/simple" } 590 | sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } 591 | wheels = [ 592 | { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, 593 | ] 594 | 595 | [[package]] 596 | name = "pandas" 597 | version = "2.2.3" 598 | source = { registry = "https://pypi.org/simple" } 599 | dependencies = [ 600 | { name = "numpy" }, 601 | { name = "python-dateutil" }, 602 | { name = "pytz" }, 603 | { name = "tzdata" }, 604 | ] 605 | sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } 606 | wheels = [ 607 | { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, 608 | { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, 609 | { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, 610 | { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, 611 | { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, 612 | { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, 613 | { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, 614 | { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, 615 | { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, 616 | { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, 617 | { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, 618 | { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, 619 | { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, 620 | ] 621 | 622 | [[package]] 623 | name = "pandas-stubs" 624 | version = "2.2.3.241126" 625 | source = { registry = "https://pypi.org/simple" } 626 | dependencies = [ 627 | { name = "numpy" }, 628 | { name = "types-pytz" }, 629 | ] 630 | sdist = { url = "https://files.pythonhosted.org/packages/90/86/93c545d149c3e1fe1c4c55478cc3a69859d0ea3467e1d9892e9eb28cb1e7/pandas_stubs-2.2.3.241126.tar.gz", hash = "sha256:cf819383c6d9ae7d4dabf34cd47e1e45525bb2f312e6ad2939c2c204cb708acd", size = 104204 } 631 | wheels = [ 632 | { url = "https://files.pythonhosted.org/packages/6f/ab/ed42acf15bab2e86e5c49fad4aa038315233c4c2d22f41b49faa4d837516/pandas_stubs-2.2.3.241126-py3-none-any.whl", hash = "sha256:74aa79c167af374fe97068acc90776c0ebec5266a6e5c69fe11e9c2cf51f2267", size = 158280 }, 633 | ] 634 | 635 | [[package]] 636 | name = "parso" 637 | version = "0.8.4" 638 | source = { registry = "https://pypi.org/simple" } 639 | sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } 640 | wheels = [ 641 | { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, 642 | ] 643 | 644 | [[package]] 645 | name = "pathspec" 646 | version = "0.12.1" 647 | source = { registry = "https://pypi.org/simple" } 648 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } 649 | wheels = [ 650 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, 651 | ] 652 | 653 | [[package]] 654 | name = "pdbpp" 655 | version = "0.10.3" 656 | source = { registry = "https://pypi.org/simple" } 657 | dependencies = [ 658 | { name = "fancycompleter" }, 659 | { name = "pygments" }, 660 | { name = "wmctrl" }, 661 | ] 662 | sdist = { url = "https://files.pythonhosted.org/packages/1f/a3/c4bd048256fd4b7d28767ca669c505e156f24d16355505c62e6fce3314df/pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5", size = 68116 } 663 | wheels = [ 664 | { url = "https://files.pythonhosted.org/packages/93/ee/491e63a57fffa78b9de1c337b06c97d0cd0753e88c00571c7b011680332a/pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1", size = 23961 }, 665 | ] 666 | 667 | [[package]] 668 | name = "pexpect" 669 | version = "4.9.0" 670 | source = { registry = "https://pypi.org/simple" } 671 | dependencies = [ 672 | { name = "ptyprocess" }, 673 | ] 674 | sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } 675 | wheels = [ 676 | { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, 677 | ] 678 | 679 | [[package]] 680 | name = "pillow" 681 | version = "11.1.0" 682 | source = { registry = "https://pypi.org/simple" } 683 | sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } 684 | wheels = [ 685 | { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, 686 | { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, 687 | { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, 688 | { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, 689 | { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, 690 | { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, 691 | { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, 692 | { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, 693 | { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, 694 | { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, 695 | { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, 696 | { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, 697 | { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, 698 | { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, 699 | { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, 700 | { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, 701 | { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, 702 | { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, 703 | { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, 704 | ] 705 | 706 | [[package]] 707 | name = "platformdirs" 708 | version = "4.3.6" 709 | source = { registry = "https://pypi.org/simple" } 710 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } 711 | wheels = [ 712 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, 713 | ] 714 | 715 | [[package]] 716 | name = "prompt-toolkit" 717 | version = "3.0.50" 718 | source = { registry = "https://pypi.org/simple" } 719 | dependencies = [ 720 | { name = "wcwidth" }, 721 | ] 722 | sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } 723 | wheels = [ 724 | { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, 725 | ] 726 | 727 | [[package]] 728 | name = "protobuf" 729 | version = "5.29.3" 730 | source = { registry = "https://pypi.org/simple" } 731 | sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 } 732 | wheels = [ 733 | { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 }, 734 | { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 }, 735 | { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 }, 736 | { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 }, 737 | { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 }, 738 | { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 }, 739 | ] 740 | 741 | [[package]] 742 | name = "ptyprocess" 743 | version = "0.7.0" 744 | source = { registry = "https://pypi.org/simple" } 745 | sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } 746 | wheels = [ 747 | { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, 748 | ] 749 | 750 | [[package]] 751 | name = "pure-eval" 752 | version = "0.2.3" 753 | source = { registry = "https://pypi.org/simple" } 754 | sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } 755 | wheels = [ 756 | { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, 757 | ] 758 | 759 | [[package]] 760 | name = "pyarrow" 761 | version = "19.0.0" 762 | source = { registry = "https://pypi.org/simple" } 763 | sdist = { url = "https://files.pythonhosted.org/packages/7b/01/fe1fd04744c2aa038e5a11c7a4adb3d62bce09798695e54f7274b5977134/pyarrow-19.0.0.tar.gz", hash = "sha256:8d47c691765cf497aaeed4954d226568563f1b3b74ff61139f2d77876717084b", size = 1129096 } 764 | wheels = [ 765 | { url = "https://files.pythonhosted.org/packages/f5/b9/ba07ed3dd6b6e4f379b78e9c47c50c8886e07862ab7fa6339ac38622d755/pyarrow-19.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:cf3bf0ce511b833f7bc5f5bb3127ba731e97222023a444b7359f3a22e2a3b463", size = 30651291 }, 766 | { url = "https://files.pythonhosted.org/packages/ad/10/0d304243c8277035298a68a70807efb76199c6c929bb3363c92ac9be6a0d/pyarrow-19.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:4d8b0c0de0a73df1f1bf439af1b60f273d719d70648e898bc077547649bb8352", size = 32100461 }, 767 | { url = "https://files.pythonhosted.org/packages/8a/61/bcfc5182e11831bca3f849945b9b106e09fd10ded773dff466658e972a45/pyarrow-19.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92aff08e23d281c69835e4a47b80569242a504095ef6a6223c1f6bb8883431d", size = 41132491 }, 768 | { url = "https://files.pythonhosted.org/packages/8e/87/2915a29049ec352dc69a967fbcbd76b0180319233de0daf8bd368df37099/pyarrow-19.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3b78eff5968a1889a0f3bc81ca57e1e19b75f664d9c61a42a604bf9d8402aae", size = 42192529 }, 769 | { url = "https://files.pythonhosted.org/packages/48/18/44e5542b2707a8afaf78b5b88c608f261871ae77787eac07b7c679ca6f0f/pyarrow-19.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b34d3bde38eba66190b215bae441646330f8e9da05c29e4b5dd3e41bde701098", size = 40495363 }, 770 | { url = "https://files.pythonhosted.org/packages/ba/d6/5096deb7599bbd20bc2768058fe23bc725b88eb41bee58303293583a2935/pyarrow-19.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5418d4d0fab3a0ed497bad21d17a7973aad336d66ad4932a3f5f7480d4ca0c04", size = 42074075 }, 771 | { url = "https://files.pythonhosted.org/packages/2c/df/e3c839c04c284c9ec3d62b02a8c452b795d9b07b04079ab91ce33484d4c5/pyarrow-19.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e82c3d5e44e969c217827b780ed8faf7ac4c53f934ae9238872e749fa531f7c9", size = 25239803 }, 772 | { url = "https://files.pythonhosted.org/packages/6a/d3/a6d4088e906c7b5d47792256212606d2ae679046dc750eee0ae167338e5c/pyarrow-19.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:f208c3b58a6df3b239e0bb130e13bc7487ed14f39a9ff357b6415e3f6339b560", size = 30695401 }, 773 | { url = "https://files.pythonhosted.org/packages/94/25/70040fd0e397dd1b937f459eaeeec942a76027357491dca0ada09d1322af/pyarrow-19.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:c751c1c93955b7a84c06794df46f1cec93e18610dcd5ab7d08e89a81df70a849", size = 32104680 }, 774 | { url = "https://files.pythonhosted.org/packages/4e/f9/92783290cc0d80ca16d34b0c126305bfacca4b87dd889c8f16c6ef2a8fd7/pyarrow-19.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b903afaa5df66d50fc38672ad095806443b05f202c792694f3a604ead7c6ea6e", size = 41076754 }, 775 | { url = "https://files.pythonhosted.org/packages/05/46/2c9870f50a495c72e2b8982ae29a9b1680707ea936edc0de444cec48f875/pyarrow-19.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22a4bc0937856263df8b94f2f2781b33dd7f876f787ed746608e06902d691a5", size = 42163133 }, 776 | { url = "https://files.pythonhosted.org/packages/7b/2f/437922b902549228fb15814e8a26105bff2787ece466a8d886eb6699efad/pyarrow-19.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:5e8a28b918e2e878c918f6d89137386c06fe577cd08d73a6be8dafb317dc2d73", size = 40452210 }, 777 | { url = "https://files.pythonhosted.org/packages/36/ef/1d7975053af9d106da973bac142d0d4da71b7550a3576cc3e0b3f444d21a/pyarrow-19.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:29cd86c8001a94f768f79440bf83fee23963af5e7bc68ce3a7e5f120e17edf89", size = 42077618 }, 778 | ] 779 | 780 | [[package]] 781 | name = "pydeck" 782 | version = "0.9.1" 783 | source = { registry = "https://pypi.org/simple" } 784 | dependencies = [ 785 | { name = "jinja2" }, 786 | { name = "numpy" }, 787 | ] 788 | sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } 789 | wheels = [ 790 | { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, 791 | ] 792 | 793 | [[package]] 794 | name = "pygments" 795 | version = "2.19.1" 796 | source = { registry = "https://pypi.org/simple" } 797 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 798 | wheels = [ 799 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 800 | ] 801 | 802 | [[package]] 803 | name = "pymdown-extensions" 804 | version = "10.14.2" 805 | source = { registry = "https://pypi.org/simple" } 806 | dependencies = [ 807 | { name = "markdown" }, 808 | { name = "pyyaml" }, 809 | ] 810 | sdist = { url = "https://files.pythonhosted.org/packages/aa/7b/de388047c577e43dc45ce35c23b9b349ec3df8c7023c3e3c4d413a850982/pymdown_extensions-10.14.2.tar.gz", hash = "sha256:7a77b8116dc04193f2c01143760a43387bd9dc4aa05efacb7d838885a7791253", size = 846777 } 811 | wheels = [ 812 | { url = "https://files.pythonhosted.org/packages/e7/a3/61527d80d84e9fd4d97649322e83bd7efde8200fc07fe34469c8c2bd0d91/pymdown_extensions-10.14.2-py3-none-any.whl", hash = "sha256:f45bc5892410e54fd738ab8ccd736098b7ff0cb27fdb4bf24d0a0c6584bc90e1", size = 264459 }, 813 | ] 814 | 815 | [[package]] 816 | name = "pyproject-hooks" 817 | version = "1.2.0" 818 | source = { registry = "https://pypi.org/simple" } 819 | sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } 820 | wheels = [ 821 | { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, 822 | ] 823 | 824 | [[package]] 825 | name = "pyreadline" 826 | version = "2.1" 827 | source = { registry = "https://pypi.org/simple" } 828 | sdist = { url = "https://files.pythonhosted.org/packages/bc/7c/d724ef1ec3ab2125f38a1d53285745445ec4a8f19b9bb0761b4064316679/pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", size = 109189 } 829 | 830 | [[package]] 831 | name = "pyrepl" 832 | version = "0.9.0" 833 | source = { registry = "https://pypi.org/simple" } 834 | sdist = { url = "https://files.pythonhosted.org/packages/05/1b/ea40363be0056080454cdbabe880773c3c5bd66d7b13f0c8b8b8c8da1e0c/pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775", size = 48744 } 835 | 836 | [[package]] 837 | name = "pyright" 838 | version = "1.1.393" 839 | source = { registry = "https://pypi.org/simple" } 840 | dependencies = [ 841 | { name = "nodeenv" }, 842 | { name = "typing-extensions" }, 843 | ] 844 | sdist = { url = "https://files.pythonhosted.org/packages/f4/c1/aede6c74e664ab103673e4f1b7fd3d058fef32276be5c43572f4067d4a8e/pyright-1.1.393.tar.gz", hash = "sha256:aeeb7ff4e0364775ef416a80111613f91a05c8e01e58ecfefc370ca0db7aed9c", size = 3790430 } 845 | wheels = [ 846 | { url = "https://files.pythonhosted.org/packages/92/47/f0dd0f8afce13d92e406421ecac6df0990daee84335fc36717678577d3e0/pyright-1.1.393-py3-none-any.whl", hash = "sha256:8320629bb7a44ca90944ba599390162bf59307f3d9fb6e27da3b7011b8c17ae5", size = 5646057 }, 847 | ] 848 | 849 | [[package]] 850 | name = "python-dateutil" 851 | version = "2.9.0.post0" 852 | source = { registry = "https://pypi.org/simple" } 853 | dependencies = [ 854 | { name = "six" }, 855 | ] 856 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } 857 | wheels = [ 858 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, 859 | ] 860 | 861 | [[package]] 862 | name = "pytz" 863 | version = "2025.1" 864 | source = { registry = "https://pypi.org/simple" } 865 | sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } 866 | wheels = [ 867 | { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, 868 | ] 869 | 870 | [[package]] 871 | name = "pyyaml" 872 | version = "6.0.2" 873 | source = { registry = "https://pypi.org/simple" } 874 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } 875 | wheels = [ 876 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, 877 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, 878 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, 879 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, 880 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, 881 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, 882 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, 883 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, 884 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, 885 | ] 886 | 887 | [[package]] 888 | name = "pyyaml-env-tag" 889 | version = "0.1" 890 | source = { registry = "https://pypi.org/simple" } 891 | dependencies = [ 892 | { name = "pyyaml" }, 893 | ] 894 | sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } 895 | wheels = [ 896 | { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, 897 | ] 898 | 899 | [[package]] 900 | name = "referencing" 901 | version = "0.36.2" 902 | source = { registry = "https://pypi.org/simple" } 903 | dependencies = [ 904 | { name = "attrs" }, 905 | { name = "rpds-py" }, 906 | ] 907 | sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } 908 | wheels = [ 909 | { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, 910 | ] 911 | 912 | [[package]] 913 | name = "regex" 914 | version = "2024.11.6" 915 | source = { registry = "https://pypi.org/simple" } 916 | sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } 917 | wheels = [ 918 | { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, 919 | { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, 920 | { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, 921 | { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, 922 | { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, 923 | { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, 924 | { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, 925 | { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, 926 | { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, 927 | { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, 928 | { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, 929 | { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, 930 | { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, 931 | { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, 932 | { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, 933 | ] 934 | 935 | [[package]] 936 | name = "requests" 937 | version = "2.32.3" 938 | source = { registry = "https://pypi.org/simple" } 939 | dependencies = [ 940 | { name = "certifi" }, 941 | { name = "charset-normalizer" }, 942 | { name = "idna" }, 943 | { name = "urllib3" }, 944 | ] 945 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 946 | wheels = [ 947 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 948 | ] 949 | 950 | [[package]] 951 | name = "rich" 952 | version = "13.9.4" 953 | source = { registry = "https://pypi.org/simple" } 954 | dependencies = [ 955 | { name = "markdown-it-py" }, 956 | { name = "pygments" }, 957 | ] 958 | sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } 959 | wheels = [ 960 | { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, 961 | ] 962 | 963 | [[package]] 964 | name = "rpds-py" 965 | version = "0.22.3" 966 | source = { registry = "https://pypi.org/simple" } 967 | sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } 968 | wheels = [ 969 | { url = "https://files.pythonhosted.org/packages/d0/bf/36d5cc1f2c609ae6e8bf0fc35949355ca9d8790eceb66e6385680c951e60/rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", size = 351657 }, 970 | { url = "https://files.pythonhosted.org/packages/24/2a/f1e0fa124e300c26ea9382e59b2d582cba71cedd340f32d1447f4f29fa4e/rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", size = 341829 }, 971 | { url = "https://files.pythonhosted.org/packages/cf/c2/0da1231dd16953845bed60d1a586fcd6b15ceaeb965f4d35cdc71f70f606/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", size = 384220 }, 972 | { url = "https://files.pythonhosted.org/packages/c7/73/a4407f4e3a00a9d4b68c532bf2d873d6b562854a8eaff8faa6133b3588ec/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", size = 391009 }, 973 | { url = "https://files.pythonhosted.org/packages/a9/c3/04b7353477ab360fe2563f5f0b176d2105982f97cd9ae80a9c5a18f1ae0f/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", size = 426989 }, 974 | { url = "https://files.pythonhosted.org/packages/8d/e6/e4b85b722bcf11398e17d59c0f6049d19cd606d35363221951e6d625fcb0/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", size = 441544 }, 975 | { url = "https://files.pythonhosted.org/packages/27/fc/403e65e56f65fff25f2973216974976d3f0a5c3f30e53758589b6dc9b79b/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", size = 385179 }, 976 | { url = "https://files.pythonhosted.org/packages/57/9b/2be9ff9700d664d51fd96b33d6595791c496d2778cb0b2a634f048437a55/rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", size = 415103 }, 977 | { url = "https://files.pythonhosted.org/packages/bb/a5/03c2ad8ca10994fcf22dd2150dd1d653bc974fa82d9a590494c84c10c641/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", size = 560916 }, 978 | { url = "https://files.pythonhosted.org/packages/ba/2e/be4fdfc8b5b576e588782b56978c5b702c5a2307024120d8aeec1ab818f0/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", size = 587062 }, 979 | { url = "https://files.pythonhosted.org/packages/67/e0/2034c221937709bf9c542603d25ad43a68b4b0a9a0c0b06a742f2756eb66/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", size = 555734 }, 980 | { url = "https://files.pythonhosted.org/packages/ea/ce/240bae07b5401a22482b58e18cfbabaa392409b2797da60223cca10d7367/rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", size = 220663 }, 981 | { url = "https://files.pythonhosted.org/packages/cb/f0/d330d08f51126330467edae2fa4efa5cec8923c87551a79299380fdea30d/rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", size = 235503 }, 982 | { url = "https://files.pythonhosted.org/packages/f7/c4/dbe1cc03df013bf2feb5ad00615038050e7859f381e96fb5b7b4572cd814/rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", size = 347698 }, 983 | { url = "https://files.pythonhosted.org/packages/a4/3a/684f66dd6b0f37499cad24cd1c0e523541fd768576fa5ce2d0a8799c3cba/rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", size = 337330 }, 984 | { url = "https://files.pythonhosted.org/packages/82/eb/e022c08c2ce2e8f7683baa313476492c0e2c1ca97227fe8a75d9f0181e95/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", size = 380022 }, 985 | { url = "https://files.pythonhosted.org/packages/e4/21/5a80e653e4c86aeb28eb4fea4add1f72e1787a3299687a9187105c3ee966/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", size = 390754 }, 986 | { url = "https://files.pythonhosted.org/packages/37/a4/d320a04ae90f72d080b3d74597074e62be0a8ecad7d7321312dfe2dc5a6a/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", size = 423840 }, 987 | { url = "https://files.pythonhosted.org/packages/87/70/674dc47d93db30a6624279284e5631be4c3a12a0340e8e4f349153546728/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", size = 438970 }, 988 | { url = "https://files.pythonhosted.org/packages/3f/64/9500f4d66601d55cadd21e90784cfd5d5f4560e129d72e4339823129171c/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", size = 383146 }, 989 | { url = "https://files.pythonhosted.org/packages/4d/45/630327addb1d17173adcf4af01336fd0ee030c04798027dfcb50106001e0/rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", size = 408294 }, 990 | { url = "https://files.pythonhosted.org/packages/5f/ef/8efb3373cee54ea9d9980b772e5690a0c9e9214045a4e7fa35046e399fee/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", size = 556345 }, 991 | { url = "https://files.pythonhosted.org/packages/54/01/151d3b9ef4925fc8f15bfb131086c12ec3c3d6dd4a4f7589c335bf8e85ba/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", size = 582292 }, 992 | { url = "https://files.pythonhosted.org/packages/30/89/35fc7a6cdf3477d441c7aca5e9bbf5a14e0f25152aed7f63f4e0b141045d/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", size = 553855 }, 993 | { url = "https://files.pythonhosted.org/packages/8f/e0/830c02b2457c4bd20a8c5bb394d31d81f57fbefce2dbdd2e31feff4f7003/rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", size = 219100 }, 994 | { url = "https://files.pythonhosted.org/packages/f8/30/7ac943f69855c2db77407ae363484b915d861702dbba1aa82d68d57f42be/rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", size = 233794 }, 995 | ] 996 | 997 | [[package]] 998 | name = "setuptools" 999 | version = "75.8.0" 1000 | source = { registry = "https://pypi.org/simple" } 1001 | sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } 1002 | wheels = [ 1003 | { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "six" 1008 | version = "1.17.0" 1009 | source = { registry = "https://pypi.org/simple" } 1010 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } 1011 | wheels = [ 1012 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "smmap" 1017 | version = "5.0.2" 1018 | source = { registry = "https://pypi.org/simple" } 1019 | sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } 1020 | wheels = [ 1021 | { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "sqlalchemy" 1026 | version = "2.0.37" 1027 | source = { registry = "https://pypi.org/simple" } 1028 | dependencies = [ 1029 | { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, 1030 | { name = "typing-extensions" }, 1031 | ] 1032 | sdist = { url = "https://files.pythonhosted.org/packages/3b/20/93ea2518df4d7a14ebe9ace9ab8bb92aaf7df0072b9007644de74172b06c/sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb", size = 9626249 } 1033 | wheels = [ 1034 | { url = "https://files.pythonhosted.org/packages/45/d1/e63e56ceab148e69f545703a74b90c8c6dc0a04a857e4e63a4c07a23cf91/SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658", size = 2097968 }, 1035 | { url = "https://files.pythonhosted.org/packages/fd/e5/93ce63310347062bd42aaa8b6785615c78539787ef4380252fcf8e2dcee3/SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb", size = 2088445 }, 1036 | { url = "https://files.pythonhosted.org/packages/1b/8c/d0e0081c09188dd26040fc8a09c7d87f539e1964df1ac60611b98ff2985a/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4", size = 3174880 }, 1037 | { url = "https://files.pythonhosted.org/packages/79/f7/3396038d8d4ea92c72f636a007e2fac71faae0b59b7e21af46b635243d09/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94", size = 3188226 }, 1038 | { url = "https://files.pythonhosted.org/packages/ef/33/7a1d85716b29c86a744ed43690e243cb0e9c32e3b68a67a97eaa6b49ef66/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0", size = 3121425 }, 1039 | { url = "https://files.pythonhosted.org/packages/27/11/fa63a77c88eb2f79bb8b438271fbacd66a546a438e4eaba32d62f11298e2/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6", size = 3149589 }, 1040 | { url = "https://files.pythonhosted.org/packages/b6/04/fcdd103b6871f2110460b8275d1c4828daa806997b0fa5a01c1cd7fd522d/SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2", size = 2070746 }, 1041 | { url = "https://files.pythonhosted.org/packages/d4/7c/e024719205bdc1465b7b7d3d22ece8e1ad57bc7d76ef6ed78bb5f812634a/SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2", size = 2094612 }, 1042 | { url = "https://files.pythonhosted.org/packages/3b/36/59cc97c365f2f79ac9f3f51446cae56dfd82c4f2dd98497e6be6de20fb91/SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1", size = 1894113 }, 1043 | ] 1044 | 1045 | [[package]] 1046 | name = "stack-data" 1047 | version = "0.6.3" 1048 | source = { registry = "https://pypi.org/simple" } 1049 | dependencies = [ 1050 | { name = "asttokens" }, 1051 | { name = "executing" }, 1052 | { name = "pure-eval" }, 1053 | ] 1054 | sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } 1055 | wheels = [ 1056 | { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "streamlit" 1061 | version = "1.41.1" 1062 | source = { registry = "https://pypi.org/simple" } 1063 | dependencies = [ 1064 | { name = "altair" }, 1065 | { name = "blinker" }, 1066 | { name = "cachetools" }, 1067 | { name = "click" }, 1068 | { name = "gitpython" }, 1069 | { name = "numpy" }, 1070 | { name = "packaging" }, 1071 | { name = "pandas" }, 1072 | { name = "pillow" }, 1073 | { name = "protobuf" }, 1074 | { name = "pyarrow" }, 1075 | { name = "pydeck" }, 1076 | { name = "requests" }, 1077 | { name = "rich" }, 1078 | { name = "tenacity" }, 1079 | { name = "toml" }, 1080 | { name = "tornado" }, 1081 | { name = "typing-extensions" }, 1082 | { name = "watchdog", marker = "sys_platform != 'darwin'" }, 1083 | ] 1084 | sdist = { url = "https://files.pythonhosted.org/packages/78/33/14b5ac0369ecf0af675911e5e84b934e6fcc2cec850857d2390eb373b0a6/streamlit-1.41.1.tar.gz", hash = "sha256:6626d32b098ba1458b71eebdd634c62af2dd876380e59c4b6a1e828a39d62d69", size = 8712473 } 1085 | wheels = [ 1086 | { url = "https://files.pythonhosted.org/packages/c2/87/b2e162869500062a94dde7589c167367b5538dab6eacce2e7c0f00d5c9c5/streamlit-1.41.1-py2.py3-none-any.whl", hash = "sha256:0def00822480071d642e6df36cd63c089f991da3a69fd9eb4ab8f65ce27de4e0", size = 9100386 }, 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "streamlit-antd-components" 1091 | version = "0.3.2" 1092 | source = { registry = "https://pypi.org/simple" } 1093 | dependencies = [ 1094 | { name = "streamlit" }, 1095 | ] 1096 | wheels = [ 1097 | { url = "https://files.pythonhosted.org/packages/b5/dc/1ed6266b606e3b494b9af3e2c310ea6cbe2e967aa18873d03c5b267b4c81/streamlit_antd_components-0.3.2-py3-none-any.whl", hash = "sha256:5ae28496127202ed266ea167649436a15f3d548a4805ee5d992c6fc0fe103fd6", size = 2805502 }, 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "streamlit-datalist" 1102 | version = "0.0.5" 1103 | source = { registry = "https://pypi.org/simple" } 1104 | dependencies = [ 1105 | { name = "streamlit" }, 1106 | ] 1107 | sdist = { url = "https://files.pythonhosted.org/packages/4e/18/d25be35c1be8566d55c723dfd6d5a745d3eeb14f9c70cfe1f20798ec29af/streamlit_datalist-0.0.5.tar.gz", hash = "sha256:281b6042e3cc1cb74fa16993754ce60dcf40ee702d076aecac682cff7f8e08fa", size = 499844 } 1108 | wheels = [ 1109 | { url = "https://files.pythonhosted.org/packages/48/ff/5e59c9e69c0a198f99b366dee3fe0690c7b73e901810f796499dc00633ae/streamlit_datalist-0.0.5-py3-none-any.whl", hash = "sha256:40b135b41e7482816ca2dc06ac534ed840b0e1dff21af8a54cb800646a400e58", size = 974990 }, 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "streamlit-sql" 1114 | version = "0.3.2" 1115 | source = { editable = "." } 1116 | dependencies = [ 1117 | { name = "loguru" }, 1118 | { name = "pandas" }, 1119 | { name = "python-dateutil" }, 1120 | { name = "sqlalchemy" }, 1121 | { name = "streamlit" }, 1122 | { name = "streamlit-antd-components" }, 1123 | { name = "streamlit-datalist" }, 1124 | ] 1125 | 1126 | [package.dev-dependencies] 1127 | dev = [ 1128 | { name = "build" }, 1129 | { name = "ipython" }, 1130 | { name = "isort" }, 1131 | { name = "mkdocs" }, 1132 | { name = "mkdocs-material" }, 1133 | { name = "mkdocstrings-python" }, 1134 | { name = "pandas-stubs" }, 1135 | { name = "pdbpp" }, 1136 | { name = "pyright" }, 1137 | { name = "rich" }, 1138 | { name = "setuptools" }, 1139 | { name = "vermin" }, 1140 | { name = "vulture" }, 1141 | ] 1142 | 1143 | [package.metadata] 1144 | requires-dist = [ 1145 | { name = "loguru", specifier = ">=0.7.3" }, 1146 | { name = "pandas" }, 1147 | { name = "python-dateutil" }, 1148 | { name = "sqlalchemy" }, 1149 | { name = "streamlit" }, 1150 | { name = "streamlit-antd-components" }, 1151 | { name = "streamlit-datalist" }, 1152 | ] 1153 | 1154 | [package.metadata.requires-dev] 1155 | dev = [ 1156 | { name = "build" }, 1157 | { name = "ipython" }, 1158 | { name = "isort" }, 1159 | { name = "mkdocs" }, 1160 | { name = "mkdocs-material", specifier = ">=9.6.1" }, 1161 | { name = "mkdocstrings-python", specifier = ">=1.13.0" }, 1162 | { name = "pandas-stubs" }, 1163 | { name = "pdbpp" }, 1164 | { name = "pyright" }, 1165 | { name = "rich" }, 1166 | { name = "setuptools" }, 1167 | { name = "vermin" }, 1168 | { name = "vulture" }, 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "tenacity" 1173 | version = "9.0.0" 1174 | source = { registry = "https://pypi.org/simple" } 1175 | sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } 1176 | wheels = [ 1177 | { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "toml" 1182 | version = "0.10.2" 1183 | source = { registry = "https://pypi.org/simple" } 1184 | sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } 1185 | wheels = [ 1186 | { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "tornado" 1191 | version = "6.4.2" 1192 | source = { registry = "https://pypi.org/simple" } 1193 | sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } 1194 | wheels = [ 1195 | { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, 1196 | { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, 1197 | { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, 1198 | { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, 1199 | { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, 1200 | { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, 1201 | { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, 1202 | { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, 1203 | { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, 1204 | { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, 1205 | ] 1206 | 1207 | [[package]] 1208 | name = "traitlets" 1209 | version = "5.14.3" 1210 | source = { registry = "https://pypi.org/simple" } 1211 | sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } 1212 | wheels = [ 1213 | { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "types-pytz" 1218 | version = "2024.2.0.20241221" 1219 | source = { registry = "https://pypi.org/simple" } 1220 | sdist = { url = "https://files.pythonhosted.org/packages/54/26/516311b02b5a215e721155fb65db8a965d061372e388d6125ebce8d674b0/types_pytz-2024.2.0.20241221.tar.gz", hash = "sha256:06d7cde9613e9f7504766a0554a270c369434b50e00975b3a4a0f6eed0f2c1a9", size = 10213 } 1221 | wheels = [ 1222 | { url = "https://files.pythonhosted.org/packages/74/db/c92ca6920cccd9c2998b013601542e2ac5e59bc805bcff94c94ad254b7df/types_pytz-2024.2.0.20241221-py3-none-any.whl", hash = "sha256:8fc03195329c43637ed4f593663df721fef919b60a969066e22606edf0b53ad5", size = 10008 }, 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "typing-extensions" 1227 | version = "4.12.2" 1228 | source = { registry = "https://pypi.org/simple" } 1229 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 1230 | wheels = [ 1231 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "tzdata" 1236 | version = "2025.1" 1237 | source = { registry = "https://pypi.org/simple" } 1238 | sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 } 1239 | wheels = [ 1240 | { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 }, 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "urllib3" 1245 | version = "2.3.0" 1246 | source = { registry = "https://pypi.org/simple" } 1247 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } 1248 | wheels = [ 1249 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "vermin" 1254 | version = "1.6.0" 1255 | source = { registry = "https://pypi.org/simple" } 1256 | sdist = { url = "https://files.pythonhosted.org/packages/3d/26/7b871396c33006c445c25ef7da605ecbd6cef830d577b496d2b73a554f9d/vermin-1.6.0.tar.gz", hash = "sha256:6266ca02f55d1c2aa189a610017c132eb2d1934f09e72a955b1eb3820ee6d4ef", size = 93181 } 1257 | wheels = [ 1258 | { url = "https://files.pythonhosted.org/packages/2e/98/1a2ca43e6d646421eea16ec19977e2e6d1ea9079bd9d873bfae513d43f1c/vermin-1.6.0-py2.py3-none-any.whl", hash = "sha256:f1fa9ee40f59983dc40e0477eb2b1fa8061a3df4c3b2bcf349add462a5610efb", size = 90845 }, 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "vulture" 1263 | version = "2.14" 1264 | source = { registry = "https://pypi.org/simple" } 1265 | sdist = { url = "https://files.pythonhosted.org/packages/8e/25/925f35db758a0f9199113aaf61d703de891676b082bd7cf73ea01d6000f7/vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415", size = 58823 } 1266 | wheels = [ 1267 | { url = "https://files.pythonhosted.org/packages/a0/56/0cc15b8ff2613c1d5c3dc1f3f576ede1c43868c1bc2e5ccaa2d4bcd7974d/vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9", size = 28915 }, 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "watchdog" 1272 | version = "6.0.0" 1273 | source = { registry = "https://pypi.org/simple" } 1274 | sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } 1275 | wheels = [ 1276 | { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, 1277 | { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, 1278 | { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, 1279 | { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, 1280 | { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, 1281 | { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, 1282 | { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, 1283 | { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, 1284 | { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, 1285 | { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, 1286 | { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, 1287 | { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, 1288 | { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "wcwidth" 1293 | version = "0.2.13" 1294 | source = { registry = "https://pypi.org/simple" } 1295 | sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } 1296 | wheels = [ 1297 | { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "win32-setctime" 1302 | version = "1.2.0" 1303 | source = { registry = "https://pypi.org/simple" } 1304 | sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 } 1305 | wheels = [ 1306 | { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "wmctrl" 1311 | version = "0.5" 1312 | source = { registry = "https://pypi.org/simple" } 1313 | dependencies = [ 1314 | { name = "attrs" }, 1315 | ] 1316 | sdist = { url = "https://files.pythonhosted.org/packages/60/d9/6625ead93412c5ce86db1f8b4f2a70b8043e0a7c1d30099ba3c6a81641ff/wmctrl-0.5.tar.gz", hash = "sha256:7839a36b6fe9e2d6fd22304e5dc372dbced2116ba41283ea938b2da57f53e962", size = 5202 } 1317 | wheels = [ 1318 | { url = "https://files.pythonhosted.org/packages/13/ca/723e3f8185738d7947f14ee7dc663b59415c6dee43bd71575f8c7f5cd6be/wmctrl-0.5-py2.py3-none-any.whl", hash = "sha256:ae695c1863a314c899e7cf113f07c0da02a394b968c4772e1936219d9234ddd7", size = 4268 }, 1319 | ] 1320 | --------------------------------------------------------------------------------