diff --git a/.dockerignore b/.dockerignore index a030649..885f28e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ * .* !d1/blank-app-nginx.conf +!docker/checks diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..732af22 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[**/*.py] +vim_modeline = set noet ts=2 sts=2 sw=2 ai ci + +[**/meson.build] +vim_modeline = set noet ts=2 sts=2 sw=2 ai ci diff --git a/.env.examples b/.env.examples new file mode 100644 index 0000000..ea15557 --- /dev/null +++ b/.env.examples @@ -0,0 +1,3 @@ +NGINX_EXPORTER_PORTS=127.0.0.1:9113 +CHECKS_PORTS=127.0.0.1:9097 +SUBNET=172.31.0 diff --git a/.gitattributes b/.gitattributes index dc859bb..3a0b795 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ releases/tar/** filter=lfs diff=lfs merge=lfs -text releases/whl/** filter=lfs diff=lfs merge=lfs -text python/online/fxreader/pr34/commands_typed/archlinux/tests/res/*.db filter=lfs diff=lfs merge=lfs -text +python/deps/whl/** filter=lfs diff=lfs merge=lfs -text +docker/*/deps/whl/** filter=lfs diff=lfs merge=lfs -text +**/*.gpg filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 297b97b..02a7a05 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,14 @@ d2/book1/books *.tar.gz .vscode/* !.vscode/launch.json +!python/deps/whl/**/*.whl python/build python/pyproject.toml .*.kate-swp !releases/whl/*.whl +.env +!docker/*/.env +.envs +!docker/*/deps/whl/** + +!dotfiles/.vim diff --git a/Makefile b/Makefile index 9d4300f..57e4530 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ python_put_archlinux_venv: -f ../releases/whl \ online.fxreader.pr34.commands_typed.archlinux + PYTHON_PROJECTS_NAMES ?= online.fxreader.pr34 python_whl: for f in $(PYTHON_PROJECTS_NAMES); do \ diff --git a/d1/fxreader.online-certbot.service b/d1/fxreader.online-certbot.service index f249bf4..8a3179c 100644 --- a/d1/fxreader.online-certbot.service +++ b/d1/fxreader.online-certbot.service @@ -1,5 +1,8 @@ [Unit] Description=fxreader.online-certbot +Requires=fxreader.online-gateway +After=fxreader.online-gateway +PartOf=fxreader.online-gateway [Service] Type=oneshot diff --git a/d1/fxreader.online-gateway.service b/d1/fxreader.online-gateway.service index 2d8b33a..1161802 100644 --- a/d1/fxreader.online-gateway.service +++ b/d1/fxreader.online-gateway.service @@ -2,10 +2,11 @@ Description=fxreader.online-service Requires=docker.service After=docker.service +PartOf=docker.service [Service] #Type=oneshot -ExecStart=/usr/bin/docker compose up --force-recreate --remove-orphans +ExecStart=/usr/bin/docker compose up ExecStop=/usr/bin/docker compose down WorkingDirectory={{PROJECT_ROOT}} StandardOutput=null diff --git a/d1/nginx_config.py b/d1/nginx_config.py index 3572036..db87695 100644 --- a/d1/nginx_config.py +++ b/d1/nginx_config.py @@ -1,4 +1,6 @@ import json +import re +import socket import os import io import sys @@ -84,6 +86,7 @@ def forward( location_body_get = lambda target_endpoint: \ r''' proxy_set_header Host $http_host; + proxy_http_version 1.1; proxy_set_header X-Forwarded-For $t1; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; @@ -217,6 +220,23 @@ def ssl(input_json, output_conf): upstream_servers = [] server_names = [] + ssh_proxy_download_rate = ssl_nginx['stream_server'].get( + 'ssh_proxy_download_rate', + 128 * 1024, + ) + ssh_proxy_upload_rate = ssl_nginx['stream_server'].get( + 'ssh_proxy_upload_rate', + 128 * 1024, + ) + web_proxy_download_rate = ssl_nginx['stream_server'].get( + 'web_proxy_download_rate', + 128 * 1024 * 1024, + ) + web_proxy_upload_rate = ssl_nginx['stream_server'].get( + 'web_proxy_upload_rate', + 128 * 1024 * 1024, + ) + if 'by_server_name' in ssl_nginx['stream_server']: for k, v in ssl_nginx['stream_server']['by_server_name'].items(): upstream_servers.append( @@ -257,6 +277,15 @@ stream { "TLSv1.3" $upstream_server_name; } + map $upstream_protocol $proxy_download_rate { + web {web_proxy_download_rate}; + ssh {ssh_proxy_download_rate}; + } + map $upstream_protocol $proxy_upload_rate { + web {web_proxy_upload_rate}; + ssh {ssh_proxy_upload_rate}; + } + map $ssl_preread_server_name $upstream_server_name { default web; {server_names} @@ -267,7 +296,12 @@ stream { listen 443; ssl_preread on; + proxy_pass $upstream_protocol; + + proxy_download_rate $proxy_download_rate; + proxy_upload_rate $proxy_upload_rate; + # proxy_upload_rate 10k; } } '''.replace( @@ -277,6 +311,14 @@ stream { ]), ).replace( '{ssh_section}', ssh_section, + ).replace( + '{web_proxy_download_rate}', '%d' % web_proxy_download_rate, + ).replace( + '{ssh_proxy_download_rate}', '%d' % ssh_proxy_download_rate, + ).replace( + '{web_proxy_upload_rate}', '%d' % web_proxy_upload_rate, + ).replace( + '{ssh_proxy_upload_rate}', '%d' % ssh_proxy_upload_rate, ).replace( '{server_names}', ''.join([ ' ' + o + '\n' @@ -290,8 +332,36 @@ stream { if 'default_server' in ssl_nginx: server = ssl_nginx['default_server'] + if 'metrics_allowed' in server: + metrics_allowed_ip = socket.gethostbyname(server['metrics_allowed']) + else: + metrics_allowed_ip = '127.0.0.1' + servers.append( r''' +server { + server_name _; + listen 80 default_server; + + location = /_metrics { + stub_status; + access_log off; + # allow 172.0.0.0/8; + allow {metrics_allowed_ip}; + # allow 127.0.0.1; + deny all; + } + + location ~ ^/.well-known/acme-challenge/ { + alias /var/www/; + try_files $uri =404; + } + + location ~ { + deny all; + } +} + server { set $t1 $remote_addr; if ($http_x_forwarded_for) @@ -317,14 +387,57 @@ server { '{domain_key}', server['domain_key'], ).replace( '{ssl_port}', '%d' % ssl_port, + ).replace( + '{metrics_allowed_ip}', metrics_allowed_ip ) ) for server in ssl_nginx['servers']: + location_proxy_app = r''' + location ^~ / { + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_redirect off; + proxy_buffering off; + proxy_http_version 1.1; + proxy_pass http://app:80; + } + ''' + + location_forward_ssl = r''' + location ~ { + #return 444; + return 301 https://$host$request_uri; + } + ''' + + if server.get('allow_http') in [True]: + http_location = location_proxy_app + else: + http_location = location_forward_ssl + + drop_by_user_agent = '' + + if not server.get('drop_by_user_agent') is None: + r = re.compile('^([a-zA-Z0-9\s\.\,\(\)]+)$') + user_agent_list = [ + r.match(o)[1] + for o in server.get('drop_by_user_agent') + ] + drop_by_user_agent = r''' + if ( $http_user_agent ~ ({user_agent_list}) ) { + return 444; + } + '''.replace( + '{user_agent_list}', + '|'.join(user_agent_list) + ) + servers.append( r''' - - server { set $t1 $remote_addr; if ($http_x_forwarded_for) @@ -341,14 +454,12 @@ server { try_files $uri =404; } - location ~ { - #return 444; - return 301 https://$host$request_uri; - } + {http_location} } server { set $t1 $remote_addr; + if ($http_x_forwarded_for) { set $t1 $http_x_forwarded_for; @@ -362,6 +473,8 @@ server { ssl_certificate {signed_chain_cert}; ssl_certificate_key {domain_key}; + {drop_by_user_agent} + location ^~ / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -370,6 +483,7 @@ server { proxy_set_header Connection $connection_upgrade; proxy_redirect off; proxy_buffering off; + proxy_http_version 1.1; proxy_pass http://app:80; } } @@ -381,8 +495,12 @@ server { '{client_max_body_size}', server['client_max_body_size'], ).replace( '{domain_key}', server['domain_key'], + ).replace( + '{drop_by_user_agent}', drop_by_user_agent, ).replace( '{ssl_port}', '%d' % ssl_port, + ).replace( + '{http_location}', http_location ) ) diff --git a/deps/com.github.aiortc.aiortc b/deps/com.github.aiortc.aiortc index adef10a..4c187fc 160000 --- a/deps/com.github.aiortc.aiortc +++ b/deps/com.github.aiortc.aiortc @@ -1 +1 @@ -Subproject commit adef10a8c41f5c550622879370a40f8a9e545574 +Subproject commit 4c187fc7dd17c52fb8e4f992d3985eb609eefe6a diff --git a/deps/online.fxreader.nartes.books b/deps/online.fxreader.nartes.books index 3c691ef..f2366f3 160000 --- a/deps/online.fxreader.nartes.books +++ b/deps/online.fxreader.nartes.books @@ -1 +1 @@ -Subproject commit 3c691ef68d8899edf328d5b06135c0d3b02e7940 +Subproject commit f2366f328fb8129fa6ae26d00b421025d2f090c7 diff --git a/docker-compose.yml b/docker-compose.yml index a8dc8f8..b283a56 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,24 @@ services: - ./d1/:/app/d1/:ro - ./tmp/cache/:/app/tmp/cache/:ro restart: on-failure + networks: + network: + ipv4_address: ${SUBNET}.2 + + nginx-exporter: + image: docker.io/nginx/nginx-prometheus-exporter@sha256:6edfb73afd11f2d83ea4e8007f5068c3ffaa38078a6b0ad1339e5bd2f637aacd + #profiles: + # - podman + #env_file: + # .envs/nginx-exporter.env + environment: + SCRAPE_URI: http://ssl-app:80/_metrics + # LISTEN_ADDRESS: 0.0.0.0:9113 + ports: + - ${NGINX_EXPORTER_PORTS:-"127.0.0.1:9113"}:9113 + networks: + network: + ssl-app: build: context: . @@ -17,8 +35,30 @@ services: - ./tmp/d1/:/app/tmp/d1/:ro - ./tmp/d1/letsencrypt:/etc/letsencrypt:rw restart: on-failure + ports: + - ${SSL_APP_PORTS:-"127.0.0.1:443"}:444 + - ${APP_PORTS:-"127.0.0.1:80"}:80 + networks: + network: + checks: + build: + context: ./docker/checks + dockerfile: ./Dockerfile + init: true + env_file: + .envs/checks.patched.env + command: + - python3 + - -m + - online.fxreader.pr34.commands_typed.async_api.fastapi + ports: + - ${CHECKS_PORTS:-"127.0.0.1:80"}:80 + networks: + network: + cpanel: + image: online.fxreader.pr34.cpanel:dev build: context: . dockerfile: ./docker/cpanel/Dockerfile @@ -28,6 +68,9 @@ services: - ./d1/:/app/d1:ro - ./tmp/d1/:/app/tmp/d1/:ro restart: on-failure + networks: + network: + dynu: build: context: . @@ -40,6 +83,8 @@ services: restart: on-failure # links: # - ngrok + networks: + network: ngrok: image: wernight/ngrok #links: @@ -50,6 +95,8 @@ services: volumes: - ./tmp/cache/ngrok.yml:/home/ngrok/.ngrok2/ngrok.yml:ro restart: on-failure + networks: + network: #forward: # build: # context: . @@ -58,3 +105,13 @@ services: # - ./d1/forward.py:/app/d1/forward.py:ro # - ./tmp/cache/forward_data:/app/tmp/cache/forward_data:ro # restart: always +networks: + network: + driver: bridge + # driver_opts: + # com.docker.network.bridge.name: br-mynet # stable bridge name (optional) + ipam: + config: + - subnet: ${SUBNET}.0/24 + gateway: "${SUBNET}.1" + ip_range: "${SUBNET}.128/25" # optional: pool for containers diff --git a/docker/checks/.env b/docker/checks/.env new file mode 100644 index 0000000..28218e9 --- /dev/null +++ b/docker/checks/.env @@ -0,0 +1,5 @@ +# UVICORN_HOST=127.0.0.1 +# UVICORN_PORT=80 +# HTTP_AUTH_USERNAME=test +# HTTP_AUTH_PASSWORD=blah +APPS='["rest:get_router:"]' diff --git a/docker/checks/Dockerfile b/docker/checks/Dockerfile new file mode 100644 index 0000000..cf5b2ec --- /dev/null +++ b/docker/checks/Dockerfile @@ -0,0 +1,26 @@ +FROM alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 + +RUN apk add --no-cache python3 py3-pip + +RUN \ + --mount=type=cache,target=/root/.cache/pip \ + pip install \ + --break-system-packages \ + uv + +WORKDIR /app + +COPY requirements.txt requirements.txt + +RUN \ + --mount=type=cache,target=/root/.cache/pip \ + --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=./deps/whl,target=/app/deps/whl \ + uv pip install \ + -f deps/whl \ + -r requirements.txt \ + --break-system-packages --system + +COPY ./rest.py ./rest.py + +# CMD ["python3", "rest.py"] diff --git a/docker/checks/Makefile b/docker/checks/Makefile new file mode 100644 index 0000000..75d3cbe --- /dev/null +++ b/docker/checks/Makefile @@ -0,0 +1,28 @@ +venv_compile: + uv pip compile \ + -p 3.12 \ + --generate-hashes \ + -f deps/whl \ + requirements.in > \ + requirements.txt + +venv: + uv venv -p 3.12 .venv + uv pip install \ + -p .venv/bin/python3 \ + -f deps/whl \ + -r requirements.txt + +PYRIGHT_CMD ?= --threads 3 + +pyright: + .venv/bin/python3 \ + -m pyright \ + --pythonpath .venv/bin/python3 \ + -p pyproject.toml \ + $(PYRIGHT_CMD) \ + . + +RUFF_CMD ?= format +ruff: + .venv/bin/python3 -m ruff --config pyproject.toml $(RUFF_CMD) . diff --git a/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl new file mode 100644 index 0000000..45b043b --- /dev/null +++ b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e6ad1193bbdb859c2f0b1b407ccf944880212fd339139beede1eb31b4b2ce8a +size 73447 diff --git a/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl new file mode 100644 index 0000000..a1d2890 --- /dev/null +++ b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b70a3fb2fe6652c1ff8c14e464a5baf80a5d9da9e39204eeba40490bf13deca +size 74822 diff --git a/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl new file mode 100644 index 0000000..58b307f --- /dev/null +++ b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3066558ef4a5dd2dfda85c3d9fb7d50014774d500187da86c6cc0795fca3be10 +size 74831 diff --git a/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl new file mode 100644 index 0000000..beaf03f --- /dev/null +++ b/docker/checks/deps/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d081758fdb91fb460da5c55d3a38257122de096363a8d956ba2f91a234566010 +size 74850 diff --git a/python/pyproject.toml b/docker/checks/pyproject.toml similarity index 67% rename from python/pyproject.toml rename to docker/checks/pyproject.toml index 226eadb..b45babb 100644 --- a/python/pyproject.toml +++ b/docker/checks/pyproject.toml @@ -1,5 +1,5 @@ [project] -description = 'set of tools for software development' +description = 'checks service' requires-python = '>= 3.10' maintainers = [ { name = 'Siarhei Siniak', email = 'siarheisiniak@gmail.com' }, @@ -8,81 +8,18 @@ classifiers = [ 'Programming Language :: Python', ] -name = "online.fxreader.pr34" -# version = '0.1.5.16+27.7' -dynamic = [ - 'version', -] - -dependencies = [ - #"-r requirements.txt", - 'mypy', - 'marisa-trie', - 'pydantic', - 'pydantic-settings', - 'tomlkit', - 'pip==23.3.2', -] - -[project.optional-dependencies] -crypto = [ - 'cryptography', -] - -early = [ - 'numpy', - 'cryptography', - 'yq', - 'toml-cli', - 'ninja', - 'patchelf', - # 'tomlkit', -] - -archlinux = [ - 'solv==0.7.35', -] - -lint = [ - 'tomli', - # 'tomllib', - 'mypy', - 'pyright', - 'pyrefly', - 'ruff', - # 'tomlkit', -] - -[project.scripts] -online-fxreader-pr34-commands = 'online.fxreader.pr34.commands:commands_cli' - -[tool.online-fxreader-pr34] -early_features = ["default", "early", "lint"] - -modules = [ - { name = 'online.fxreader.pr34', tool = { 'online-fxreader-pr34' = { early_features = ['default', 'early', 'lint'] } } }, -] - -[build-system] -requires = ["meson-python", "pybind11"] -build-backend = "mesonpy" - +name = 'online.fxreader.pr34.checks' [tool.ruff] line-length = 160 target-version = 'py310' -# builtins = ['_', 'I', 'P'] include = [ - # 'follow_the_leader/**/*.py', - #'*.py', - # '*.recipe', '*.py', - 'online/**/*.py', - 'online/**/*.pyi', + '*/**/*.py', + '*/**/*.pyi', ] exclude = [ '.venv', - 'online/fxreader/pr34/commands_typed/archlinux', ] [tool.ruff.format] @@ -113,9 +50,6 @@ select = ['E', 'F', 'I', 'W', 'INT'] [tool.ruff.lint.isort] detect-same-package = true -# extra-standard-library = ["aes", "elementmaker", "encodings"] -# known-first-party = ["calibre_extensions", "calibre_plugins", "polyglot"] -# known-third-party = ["odf", "qt", "templite", "tinycss", "css_selectors"] relative-imports-order = "closest-to-furthest" split-on-trailing-comma = true section-order = [ @@ -133,21 +67,11 @@ enabled = false [tool.pyright] include = [ - #'../../../../../follow_the_leader/views2/payments.py', - #'../../../../../follow_the_leader/logic/payments.py', - #'../../../../../follow_the_leader/logic/paypal.py', - 'online/fxreader/pr34/commands_typed/**/*.py', + '*/**/*.py', ] -# stubPath = '../mypy-stubs' extraPaths = [ '.', - '../mypy-stubs', - '../mypy-stubs/types-debugpy', - '../mypy-stubs/types-solv', - '../mypy-stubs/marisa-trie-types', - # '../../../../../', ] -#strict = ["src"] analyzeUnannotatedFunctions = true disableBytesTypePromotions = true @@ -247,18 +171,3 @@ reportUninitializedInstanceVariable = "none" reportUnnecessaryTypeIgnoreComment = "none" reportUnusedCallResult = "none" -[tool.pyrefly] -project-includes = [ - 'online/fxreader/pr34/commands_typed/**/*.py', -] -project-excludes = [ - '.venv', - 'online/fxreader/pr34/commands_typed/archlinux', -] -search-path = [ - '.', - '../mypy-stubs/types-debugpy', - '../mypy-stubs/types-solv', - '../mypy-stubs/marisa-trie-types', -] -python-version = '3.13' diff --git a/docker/checks/requirements.in b/docker/checks/requirements.in new file mode 100644 index 0000000..3d0e939 --- /dev/null +++ b/docker/checks/requirements.in @@ -0,0 +1,4 @@ +online.fxreader.pr34[django,fastapi,lint]>=0.1.5.24 +fastapi +uvicorn +numpy diff --git a/docker/checks/requirements.txt b/docker/checks/requirements.txt new file mode 100644 index 0000000..f6f82a9 --- /dev/null +++ b/docker/checks/requirements.txt @@ -0,0 +1,582 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile -p 3.12 --generate-hashes requirements.in +annotated-types==0.7.0 \ + --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ + --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 + # via pydantic +anyio==4.10.0 \ + --hash=sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6 \ + --hash=sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1 + # via starlette +argcomplete==3.6.2 \ + --hash=sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591 \ + --hash=sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf + # via yq +asgiref==3.9.1 \ + --hash=sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142 \ + --hash=sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c + # via django +click==8.2.1 \ + --hash=sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202 \ + --hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b + # via uvicorn +django==5.2.5 \ + --hash=sha256:0745b25681b129a77aae3d4f6549b62d3913d74407831abaa0d9021a03954bae \ + --hash=sha256:2b2ada0ee8a5ff743a40e2b9820d1f8e24c11bac9ae6469cd548f0057ea6ddcd + # via + # django-stubs + # django-stubs-ext + # online-fxreader-pr34 +django-stubs==5.2.2 \ + --hash=sha256:2a04b510c7a812f88223fd7e6d87fb4ea98717f19c8e5c8b59691d83ad40a8a6 \ + --hash=sha256:79bd0fdbc78958a8f63e0b062bd9d03f1de539664476c0be62ade5f063c9e41e + # via online-fxreader-pr34 +django-stubs-ext==5.2.2 \ + --hash=sha256:8833bbe32405a2a0ce168d3f75a87168f61bd16939caf0e8bf173bccbd8a44c5 \ + --hash=sha256:d9d151b919fe2438760f5bd938f03e1cb08c84d0651f9e5917f1313907e42683 + # via django-stubs +fastapi==0.116.1 \ + --hash=sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565 \ + --hash=sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143 + # via + # -r requirements.in + # online-fxreader-pr34 +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via uvicorn +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 + # via anyio +marisa-trie==1.3.1 \ + --hash=sha256:076731f79f8603cb3216cb6e5bbbc56536c89f63f175ad47014219ecb01e5996 \ + --hash=sha256:0b9816ab993001a7854b02a7daec228892f35bd5ab0ac493bacbd1b80baec9f1 \ + --hash=sha256:0c2bc6bee737f4d47fce48c5b03a7bd3214ef2d83eb5c9f84210091370a5f195 \ + --hash=sha256:0dcd42774e367ceb423c211a4fc8e7ce586acfaf0929c9c06d98002112075239 \ + --hash=sha256:0e6f3b45def6ff23e254eeaa9079267004f0069d0a34eba30a620780caa4f2cb \ + --hash=sha256:137010598d8cebc53dbfb7caf59bde96c33a6af555e3e1bdbf30269b6a157e1e \ + --hash=sha256:2f7c10f69cbc3e6c7d715ec9cb0c270182ea2496063bebeda873f4aa83fd9910 \ + --hash=sha256:3715d779561699471edde70975e07b1de7dddb2816735d40ed16be4b32054188 \ + --hash=sha256:3834304fdeaa1c9b73596ad5a6c01a44fc19c13c115194704b85f7fbdf0a7b8e \ + --hash=sha256:389721481c14a92fa042e4b91ae065bff13e2bc567c85a10aa9d9de80aaa8622 \ + --hash=sha256:3a96ef3e461ecc85ec7d2233ddc449ff5a3fbdc520caea752bc5bc8faa975231 \ + --hash=sha256:3e2a0e1be95237981bd375a388f44b33d69ea5669a2f79fea038e45fff326595 \ + --hash=sha256:3e431f9c80ee1850b2a406770acf52c058b97a27968a0ed6aca45c2614d64c9f \ + --hash=sha256:47631614c5243ed7d15ae0af8245fcc0599f5b7921fae2a4ae992afb27c9afbb \ + --hash=sha256:52d1764906befef91886e3bff374d8090c9716822bd56b70e07aa697188090b7 \ + --hash=sha256:5370f9ef6c008e502537cc1ff518c80ddf749367ce90179efa0e7f6275903a76 \ + --hash=sha256:56043cf908ddf3d7364498085dbc2855d4ea8969aff3bf2439a79482a79e68e2 \ + --hash=sha256:5a6abc9573a6a45d09548fde136dbcd4260b8c56f8dff443eaa565352d7cca59 \ + --hash=sha256:5b7c1e7fa6c3b855e8cfbabf38454d7decbaba1c567d0cd58880d033c6b363bd \ + --hash=sha256:5ef045f694ef66079b4e00c4c9063a00183d6af7d1ff643de6ea5c3b0d9af01b \ + --hash=sha256:68678816818efcd4a1787b557af81f215b989ec88680a86c85c34c914d413690 \ + --hash=sha256:6cac19952e0e258ded765737d1fb11704fe81bf4f27526638a5d44496f329235 \ + --hash=sha256:70b4c96f9119cfeb4dc6a0cf4afc9f92f0b002cde225bcd910915d976c78e66a \ + --hash=sha256:7e957aa4251a8e70b9fe02a16b2d190f18787902da563cb7ba865508b8e8fb04 \ + --hash=sha256:82de2de90488d0fbbf74cf9f20e1afd62e320693b88f5e9565fc80b28f5bbad3 \ + --hash=sha256:83a3748088d117a9b15d8981c947df9e4f56eb2e4b5456ae34fe1f83666c9185 \ + --hash=sha256:83efc045fc58ca04c91a96c9b894d8a19ac6553677a76f96df01ff9f0405f53d \ + --hash=sha256:8c8b2386d2d22c57880ed20a913ceca86363765623175671137484a7d223f07a \ + --hash=sha256:8f81344d212cb41992340b0b8a67e375f44da90590b884204fd3fa5e02107df2 \ + --hash=sha256:954fef9185f8a79441b4e433695116636bf66402945cfee404f8983bafa59788 \ + --hash=sha256:9651daa1fdc471df5a5fa6a4833d3b01e76ac512eea141a5995681aebac5555f \ + --hash=sha256:9688c7b45f744366a4ef661e399f24636ebe440d315ab35d768676c59c613186 \ + --hash=sha256:97107fd12f30e4f8fea97790343a2d2d9a79d93697fe14e1b6f6363c984ff85b \ + --hash=sha256:9868b7a8e0f648d09ffe25ac29511e6e208cc5fb0d156c295385f9d5dc2a138e \ + --hash=sha256:986eaf35a7f63c878280609ecd37edf8a074f7601c199acfec81d03f1ee9a39a \ + --hash=sha256:99a00cab4cf9643a87977c87a5c8961aa44fff8d5dd46e00250135f686e7dedf \ + --hash=sha256:9c56001badaf1779afae5c24b7ab85938644ab8ef3c5fd438ab5d49621b84482 \ + --hash=sha256:9dc61fb8f8993589544f6df268229c6cf0a56ad4ed3e8585a9cd23c5ad79527b \ + --hash=sha256:9de573d933db4753a50af891bcb3ffbfe14e200406214c223aa5dfe2163f316d \ + --hash=sha256:9e467e13971c64db6aed8afe4c2a131c3f73f048bec3f788a6141216acda598d \ + --hash=sha256:9e6496bbad3068e3bbbb934b1e1307bf1a9cb4609f9ec47b57e8ea37f1b5ee40 \ + --hash=sha256:9f92d3577c72d5a97af5c8e3d98247b79c8ccfb64ebf611311dcf631b11e5604 \ + --hash=sha256:a1c6990961d1177f6d8fdf7b610fa2e7c0c02743a090d173f6dfa9dc9231c73c \ + --hash=sha256:a5a0a58ffe2a7eb3f870214c6df8f9a43ce768bd8fed883e6ba8c77645666b63 \ + --hash=sha256:a7416f1a084eb889c5792c57317875aeaa86abfe0bdc6f167712cebcec1d36ee \ + --hash=sha256:a83f5f7ae3494e0cc25211296252b1b86901c788ed82c83adda19d0c98f828d6 \ + --hash=sha256:a850b151bd1e3a5d9afef113adc22727d696603659d575d7e84f994bd8d04bf1 \ + --hash=sha256:ad82ab8a58562cf69e6b786debcc7638b28df12f9f1c7bcffb07efb5c1f09cbd \ + --hash=sha256:b173ec46d521308f7c97d96d6e05cf2088e0548f82544ec9a8656af65593304d \ + --hash=sha256:bf9f2b97fcfd5e2dbb0090d0664023872dcde990df0b545eca8d0ce95795a409 \ + --hash=sha256:c12b44c190deb0d67655021da1f2d0a7d61a257bf844101cf982e68ed344f28d \ + --hash=sha256:c6571462417cda2239b1ade86ceaf3852da9b52c6286046e87d404afc6da20a7 \ + --hash=sha256:c785fd6dae9daa6825734b7b494cdac972f958be1f9cb3fb1f32be8598d2b936 \ + --hash=sha256:c7a33506d0451112911c69f38d55da3e0e050f2be0ea4e5176865cf03baf26a9 \ + --hash=sha256:c89df75aefe1ad7e613340790130f1badc5926bcfa66a6b3c9471071002956a5 \ + --hash=sha256:ca644534f15f85bba14c412afc17de07531e79a766ce85b8dbf3f8b6e7758f20 \ + --hash=sha256:cbd28f95d5f30d9a7af6130869568e75bfd7ef2e0adfb1480f1f44480f5d3603 \ + --hash=sha256:d0f87bdf660f01e88ab3a507955697b2e3284065afa0b94fc9e77d6ad153ed5e \ + --hash=sha256:d4bd41a6e73c0d0adafe4de449b6d35530a4ce6a836a6ee839baf117785ecfd7 \ + --hash=sha256:d8d5e686db0ae758837ed29b3b742afb994d1a01ce10977eabd3490f16b5c9f9 \ + --hash=sha256:e5888b269e790356ce4525f3e8df1fe866d1497b7d7fb7548cfec883cb985288 \ + --hash=sha256:ec633e108f277f2b7f4671d933a909f39bba549910bf103e2940b87a14da2783 \ + --hash=sha256:ecdb19d33b26738a32602ef432b06cc6deeca4b498ce67ba8e5e39c8a7c19745 \ + --hash=sha256:ee428575377e29c636f2b4b3b0488875dcea310c6c5b3412ec4ef997f7bb37cc \ + --hash=sha256:f4bae4f920f2a1082eaf766c1883df7da84abdf333bafa15b8717c10416a615e + # via online-fxreader-pr34 +mypy==1.17.1 \ + --hash=sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341 \ + --hash=sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5 \ + --hash=sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849 \ + --hash=sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733 \ + --hash=sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81 \ + --hash=sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403 \ + --hash=sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6 \ + --hash=sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01 \ + --hash=sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91 \ + --hash=sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972 \ + --hash=sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8 \ + --hash=sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd \ + --hash=sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9 \ + --hash=sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0 \ + --hash=sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19 \ + --hash=sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb \ + --hash=sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd \ + --hash=sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99 \ + --hash=sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7 \ + --hash=sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056 \ + --hash=sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7 \ + --hash=sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a \ + --hash=sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed \ + --hash=sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94 \ + --hash=sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9 \ + --hash=sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58 \ + --hash=sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8 \ + --hash=sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5 \ + --hash=sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a \ + --hash=sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df \ + --hash=sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb \ + --hash=sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d \ + --hash=sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390 \ + --hash=sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b \ + --hash=sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b \ + --hash=sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14 \ + --hash=sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259 \ + --hash=sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b + # via online-fxreader-pr34 +mypy-extensions==1.1.0 \ + --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ + --hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558 + # via mypy +nodeenv==1.9.1 \ + --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ + --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 + # via pyright +numpy==2.3.2 \ + --hash=sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5 \ + --hash=sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b \ + --hash=sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631 \ + --hash=sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58 \ + --hash=sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b \ + --hash=sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc \ + --hash=sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089 \ + --hash=sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf \ + --hash=sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15 \ + --hash=sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f \ + --hash=sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3 \ + --hash=sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170 \ + --hash=sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910 \ + --hash=sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91 \ + --hash=sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45 \ + --hash=sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c \ + --hash=sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f \ + --hash=sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b \ + --hash=sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89 \ + --hash=sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a \ + --hash=sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220 \ + --hash=sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e \ + --hash=sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab \ + --hash=sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2 \ + --hash=sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b \ + --hash=sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370 \ + --hash=sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2 \ + --hash=sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee \ + --hash=sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619 \ + --hash=sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712 \ + --hash=sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1 \ + --hash=sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec \ + --hash=sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a \ + --hash=sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450 \ + --hash=sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a \ + --hash=sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2 \ + --hash=sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168 \ + --hash=sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2 \ + --hash=sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73 \ + --hash=sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296 \ + --hash=sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9 \ + --hash=sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125 \ + --hash=sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0 \ + --hash=sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19 \ + --hash=sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b \ + --hash=sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f \ + --hash=sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2 \ + --hash=sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f \ + --hash=sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a \ + --hash=sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6 \ + --hash=sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286 \ + --hash=sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981 \ + --hash=sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f \ + --hash=sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2 \ + --hash=sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0 \ + --hash=sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b \ + --hash=sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b \ + --hash=sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56 \ + --hash=sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5 \ + --hash=sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3 \ + --hash=sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8 \ + --hash=sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0 \ + --hash=sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036 \ + --hash=sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6 \ + --hash=sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8 \ + --hash=sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48 \ + --hash=sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07 \ + --hash=sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b \ + --hash=sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b \ + --hash=sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d \ + --hash=sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0 \ + --hash=sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097 \ + --hash=sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be \ + --hash=sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5 + # via -r requirements.in +online-fxreader-pr34==0.1.5.27 \ + --hash=sha256:d081758fdb91fb460da5c55d3a38257122de096363a8d956ba2f91a234566010 + # via -r requirements.in +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via mypy +pip==25.1 \ + --hash=sha256:13b4aa0aaad055020a11bec8a1c2a70a2b2d080e12d89b962266029fff0a16ba \ + --hash=sha256:272bdd1289f80165e9070a4f881e8f9e1001bbb50378561d1af20e49bf5a2200 + # via online-fxreader-pr34 +pydantic==2.11.7 \ + --hash=sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db \ + --hash=sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b + # via + # fastapi + # online-fxreader-pr34 + # pydantic-settings +pydantic-core==2.33.2 \ + --hash=sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d \ + --hash=sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac \ + --hash=sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02 \ + --hash=sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56 \ + --hash=sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4 \ + --hash=sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22 \ + --hash=sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef \ + --hash=sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec \ + --hash=sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d \ + --hash=sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b \ + --hash=sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a \ + --hash=sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f \ + --hash=sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052 \ + --hash=sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab \ + --hash=sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916 \ + --hash=sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c \ + --hash=sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf \ + --hash=sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27 \ + --hash=sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a \ + --hash=sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8 \ + --hash=sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7 \ + --hash=sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612 \ + --hash=sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1 \ + --hash=sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039 \ + --hash=sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca \ + --hash=sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7 \ + --hash=sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a \ + --hash=sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6 \ + --hash=sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782 \ + --hash=sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b \ + --hash=sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7 \ + --hash=sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025 \ + --hash=sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849 \ + --hash=sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7 \ + --hash=sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b \ + --hash=sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa \ + --hash=sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e \ + --hash=sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea \ + --hash=sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac \ + --hash=sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51 \ + --hash=sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e \ + --hash=sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162 \ + --hash=sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65 \ + --hash=sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2 \ + --hash=sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954 \ + --hash=sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b \ + --hash=sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de \ + --hash=sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc \ + --hash=sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64 \ + --hash=sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb \ + --hash=sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9 \ + --hash=sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101 \ + --hash=sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d \ + --hash=sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef \ + --hash=sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3 \ + --hash=sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1 \ + --hash=sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5 \ + --hash=sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88 \ + --hash=sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d \ + --hash=sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290 \ + --hash=sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e \ + --hash=sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d \ + --hash=sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808 \ + --hash=sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc \ + --hash=sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d \ + --hash=sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc \ + --hash=sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e \ + --hash=sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640 \ + --hash=sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30 \ + --hash=sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e \ + --hash=sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9 \ + --hash=sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a \ + --hash=sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9 \ + --hash=sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f \ + --hash=sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb \ + --hash=sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5 \ + --hash=sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab \ + --hash=sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d \ + --hash=sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572 \ + --hash=sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593 \ + --hash=sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29 \ + --hash=sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535 \ + --hash=sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1 \ + --hash=sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f \ + --hash=sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8 \ + --hash=sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf \ + --hash=sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246 \ + --hash=sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9 \ + --hash=sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011 \ + --hash=sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9 \ + --hash=sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a \ + --hash=sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3 \ + --hash=sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6 \ + --hash=sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8 \ + --hash=sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a \ + --hash=sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2 \ + --hash=sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c \ + --hash=sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6 \ + --hash=sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d + # via pydantic +pydantic-settings==2.10.1 \ + --hash=sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee \ + --hash=sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796 + # via online-fxreader-pr34 +pyright==1.1.404 \ + --hash=sha256:455e881a558ca6be9ecca0b30ce08aa78343ecc031d37a198ffa9a7a1abeb63e \ + --hash=sha256:c7b7ff1fdb7219c643079e4c3e7d4125f0dafcc19d253b47e898d130ea426419 + # via online-fxreader-pr34 +python-dotenv==1.1.1 \ + --hash=sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc \ + --hash=sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab + # via pydantic-settings +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via yq +ruff==0.12.11 \ + --hash=sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000 \ + --hash=sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee \ + --hash=sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211 \ + --hash=sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee \ + --hash=sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8 \ + --hash=sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f \ + --hash=sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793 \ + --hash=sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0 \ + --hash=sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644 \ + --hash=sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2 \ + --hash=sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065 \ + --hash=sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd \ + --hash=sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93 \ + --hash=sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea \ + --hash=sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d \ + --hash=sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3 \ + --hash=sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39 \ + --hash=sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd \ + --hash=sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9 + # via online-fxreader-pr34 +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio +sqlparse==0.5.3 \ + --hash=sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272 \ + --hash=sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca + # via django +starlette==0.47.3 \ + --hash=sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9 \ + --hash=sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51 + # via fastapi +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via online-fxreader-pr34 +tomlkit==0.13.3 \ + --hash=sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1 \ + --hash=sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0 + # via + # online-fxreader-pr34 + # yq +tomlq==0.1.0 \ + --hash=sha256:4b966fd999ed2bf69081b7c7f5caadbc4c9542d0ed5fcf2e9b7b4d8d7ada3c82 \ + --hash=sha256:e775720e90da3e405142b9fe476145e71c0389f787b1ff9933f92a1704d8c6e7 + # via online-fxreader-pr34 +types-pyyaml==6.0.12.20250822 \ + --hash=sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098 \ + --hash=sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413 + # via django-stubs +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via + # anyio + # django-stubs + # django-stubs-ext + # fastapi + # mypy + # pydantic + # pydantic-core + # pyright + # starlette + # typing-inspection +typing-inspection==0.4.1 \ + --hash=sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51 \ + --hash=sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28 + # via + # pydantic + # pydantic-settings +uvicorn==0.35.0 \ + --hash=sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a \ + --hash=sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01 + # via + # -r requirements.in + # online-fxreader-pr34 +uvloop==0.21.0 \ + --hash=sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0 \ + --hash=sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f \ + --hash=sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc \ + --hash=sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414 \ + --hash=sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f \ + --hash=sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d \ + --hash=sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd \ + --hash=sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff \ + --hash=sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c \ + --hash=sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3 \ + --hash=sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d \ + --hash=sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a \ + --hash=sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb \ + --hash=sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2 \ + --hash=sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0 \ + --hash=sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6 \ + --hash=sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c \ + --hash=sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af \ + --hash=sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc \ + --hash=sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb \ + --hash=sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75 \ + --hash=sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb \ + --hash=sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553 \ + --hash=sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e \ + --hash=sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6 \ + --hash=sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d \ + --hash=sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206 \ + --hash=sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc \ + --hash=sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281 \ + --hash=sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b \ + --hash=sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8 \ + --hash=sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79 \ + --hash=sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f \ + --hash=sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe \ + --hash=sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26 \ + --hash=sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816 \ + --hash=sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2 + # via online-fxreader-pr34 +xmltodict==0.14.2 \ + --hash=sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553 \ + --hash=sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac + # via yq +yq==3.4.3 \ + --hash=sha256:547e34bc3caacce83665fd3429bf7c85f8e8b6b9aaee3f953db1ad716ff3434d \ + --hash=sha256:ba586a1a6f30cf705b2f92206712df2281cd320280210e7b7b80adcb8f256e3b + # via tomlq diff --git a/docker/checks/rest.py b/docker/checks/rest.py new file mode 100644 index 0000000..f3cbfa5 --- /dev/null +++ b/docker/checks/rest.py @@ -0,0 +1,95 @@ +import fastapi +import re +import numpy +import subprocess +import fastapi.responses +import pydantic_settings +import logging + +logger = logging.getLogger(__name__) + +from online.fxreader.pr34.commands_typed import metrics as pr34_metrics + +from typing import Optional, Any, ClassVar + + +class Settings(pydantic_settings.BaseSettings): + checks_hosts: list[str] + + _singleton: ClassVar[Optional['Settings']] = None + + @classmethod + def singleton(cls) -> 'Settings': + if cls._singleton is None: + cls._singleton = Settings.model_validate({}) + + return cls._singleton + + +def ping_stats(host: str) -> Optional[float]: + try: + ping_output = subprocess.check_output( + [ + 'ping', + '-i', + '0.1', + '-c', + '3', + '-w', + '1', + host, + ] + ).decode('utf-8') + except: + logger.exception('') + + ping_output = '' + + r1 = re.compile(r'time=(\d+\.\d+)\sms') + + spend_time = [float(o[1]) for o in r1.finditer(ping_output)] + + if len(spend_time) == 0: + return None + else: + return float(numpy.mean(spend_time)) + + +async def metrics_get() -> fastapi.responses.Response: + ping_res = {h: ping_stats(h) for h in Settings.singleton().checks_hosts} + + metrics = [ + pr34_metrics.Metric.model_validate( + dict( + name='ping_mean', + type='gauge', + help='ping to host, 3 counts, up to 1 second', + samples=[ + dict( + value=str(v), + parameters=dict( + host=k, + ), + ) + ], + ) + ) + for k, v in ping_res.items() + if not v is None + ] + serialize_res = pr34_metrics.serialize(metrics) + + return fastapi.responses.Response( + content=serialize_res.json2, + headers={ + 'Content-Type': serialize_res.content_type, + }, + ) + + +def get_router() -> fastapi.APIRouter: + router = fastapi.APIRouter() + + router.get('/metrics')(metrics_get) + + return router diff --git a/docker/cpanel/Dockerfile b/docker/cpanel/Dockerfile index be88b41..ba1b460 100644 --- a/docker/cpanel/Dockerfile +++ b/docker/cpanel/Dockerfile @@ -1,9 +1,11 @@ -FROM alpine:latest +# FROM alpine:latest +FROM alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 RUN apk add openssh RUN apk add python3 RUN apk add tini RUN apk add bash curl RUN apk add py3-pip +RUN apk add netcat-openbsd RUN pip3 install --break-system-packages requests WORKDIR /app diff --git a/dotfiles/.py3.vimrc b/dotfiles/.py3.vimrc index d453fab..a80ca9c 100644 --- a/dotfiles/.py3.vimrc +++ b/dotfiles/.py3.vimrc @@ -1,132 +1,56 @@ -py3 << EOF -def f1(): - t1 = vim.current.window - t2 = t1.width - vim.command('vnew') - t3 = t2 // 3 - vim.command('vertical resize %d' % t3) - vim.current.window = t1 +py3 < 0: - git_cmd.extend(['--', *[ - '**/*%s' % o - for o in options['ext'] - ]]) - - completed_process = subprocess.run( - git_cmd, - capture_output=True, - ) - assert ( - completed_process.returncode == 0 or - ( - completed_process.stdout == b'' - #completed_process.stdout == b'' and - #completed_process.stderr == b'' - ) - ) - t1 = completed_process.stdout - except: - logging.error(''.join([ - traceback.format_exc(), - getattr(completed_process, 'stdout', b'').decode('utf-8'), - getattr(completed_process, 'stderr', b'').decode('utf-8'), - ])) - t1 = b'' - - def watch(data): - with tempfile.NamedTemporaryFile(suffix='.txt') as f: - with io.open(f.name, 'wb') as f2: - f2.write(data) - vim.command('!less %s' % f.name) - - #watch(t1) - - t2 = [] - for o in t1.splitlines(): - try: - #watch(o.encode('utf-8')) - t3 = o.decode('utf-8') - t4 = re.compile(r'^([^\:\=]+)[\:\=](\d+)[\:\=](.*)$').match(t3) - if not t4 is None: - t2.append( - dict( - name=t4[3].strip(), - filename=t4[1], - cmd=t4[2], - ) - ) - except: - pass - #print(t2) - - #return [{'name': 'blah', 'filename': 'docker-compose.yml', 'cmd': '23'}] - return t2 + m = importlib.import_module(o) + getattr(m, 'init')() + # vim.command('py3file {}'.format(str(o))) EOF +" py3file ~/.module.vimrc.py +python3 load() + function! F5(pattern, flags, info) + python3 import online_fxreader_pr34_vim.main; + let res = py3eval( - \'f5_1( + \'online_fxreader_pr34_vim.main.f5_1( \vim.bindeval("a:pattern").decode("utf-8"), \vim.bindeval("a:flags"), \vim.bindeval("a:info") diff --git a/dotfiles/.sway/config b/dotfiles/.sway/config index ff80706..028b6f3 100644 --- a/dotfiles/.sway/config +++ b/dotfiles/.sway/config @@ -79,7 +79,7 @@ bindgesture swipe:4:up exec $lock_cmd # # Basics: # -bindsym Shift+$mod+l exec $lock_cmd +bindsym Shift+$mod+q exec $lock_cmd bindsym --locked Shift+mod1+1 \ exec ~/.local/bin/commands \ @@ -122,12 +122,14 @@ bindsym --locked XF86AudioMute exec zsh -c "commands media-toggle-volume" bindsym --locked XF86AudioNext exec zsh -c "commands media-next" bindsym --locked XF86AudioPrev exec zsh -c "commands media-prev" +bindsym $mod+m exec zsh -c "commands color_scheme toggle" + # Start a terminal bindsym $mod+t exec $term -# Kill focused window -bindsym $mod+Shift+q kill +## Kill focused window +#bindsym $mod+Shift+q kill # Start your launcher bindsym $mod+Return exec $menu diff --git a/dotfiles/.tmux.conf b/dotfiles/.tmux.conf index e53dba3..cfd9436 100644 --- a/dotfiles/.tmux.conf +++ b/dotfiles/.tmux.conf @@ -30,6 +30,7 @@ bind -n M-[ copy-mode bind -n M-m set -g mouse set -g default-terminal "screen-256color" +set-option -g status-style "bg=#00aa00,fg=#ffffff" #set-option -ga terminal-overrides ",screen-256color:Tc" set-option -g pane-active-border-style "bg=#33dd44 fg=#ffffff" @@ -37,7 +38,8 @@ set-option -g pane-active-border-style "bg=#33dd44 fg=#ffffff" bind space display "Fuck!" set-option -g set-titles on set-option -g set-titles-string "#S / #W" -set -g status-right "#H %H:%M:%S %Y-%m-%d %Z" +# set -g status-right "#H %H:%M:%S %Y-%m-%d %Z" +set -g status-right "#{=-16:pane_current_path} #{pane_index} #H %H:%M:%S %Y-%m-%d %Z" set -g status-interval 1 -set -g status-right-length 60 +set -g status-right-length 64 set -g mouse on diff --git a/dotfiles/.vim/online_fxreader_pr34_vim/__init__.py b/dotfiles/.vim/online_fxreader_pr34_vim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dotfiles/.vim/online_fxreader_pr34_vim/beta.py b/dotfiles/.vim/online_fxreader_pr34_vim/beta.py new file mode 100644 index 0000000..ce18cfa --- /dev/null +++ b/dotfiles/.vim/online_fxreader_pr34_vim/beta.py @@ -0,0 +1,514 @@ +import functools +import configparser +import subprocess +import dataclasses +import json +import datetime +import collections +import asyncio +import threading +import re +import inspect +import pathlib +import logging +import fnmatch +import vim + +from typing import ( + Optional, + ClassVar, + Self, + Any, + Callable, +) + +from .utils import Vim + +logger = logging.getLogger(__name__) + +MODULE_NAME = 'online_fxreader_pr34_vim' + + +def future_dump_exception(future: Any) -> None: + try: + future.result() + except: + logger.exception('') + + +class FastSelect: + _instance: ClassVar[Optional['FastSelect']] = None + + def __init__(self) -> None: + self.loop = asyncio.new_event_loop() + + self.thread = threading.Thread( + target=self.loop.run_forever, + ) + + self._buffer_frequency: dict[int, int] = dict() + self._buffer_last_used: dict[int, int] = dict() + + self._filter_pattern: Optional[str] = None + self._include_git : Optional[bool] = False + self._filtered_ids: Optional[set[int]] = None + + self._items: Optional[list['self.entry_t']] = None + self._buffers: Optional[list['self.entry_t']] = None + self._tracked_files: Optional[list['self.entry_t']] = None + + self._queue: collections.deque[Callable[[], None]] = collections.deque() + self._lock = threading.Lock() + + self.popup_id: Optional[int] = None + + self.thread.start() + self._option_id: asyncio.Future[Optional[int]] = None + self._options: list[str] = None + + auto_group = '{}_{}_{}'.format( + MODULE_NAME, + type(self).__name__.lower(), + 'close', + ).capitalize() + + vim.command(r""" + func! UIThread(timer_id) + python3 online_fxreader_pr34_vim.beta.FastSelect.singleton().ui_thread() + endfunc + """) + Vim.run_command(r""" + call timer_start(100, 'UIThread', {'repeat': -1}) + """) + Vim.run_command( + r""" +augroup {auto_group} + autocmd! + autocmd VimLeavePre * python3 import online_fxreader_pr34_vim.beta; online_fxreader_pr34_vim.beta.FastSelect.singleton().close() + autocmd BufEnter * python3 import online_fxreader_pr34_vim.beta; online_fxreader_pr34_vim.beta.FastSelect.singleton().on_buf_enter() +augroup END + """.format( + auto_group=auto_group, + ) + ) + + def __del__(self) -> None: + self.close() + + def close(self) -> None: + logger.info(dict(msg='close started')) + self.loop.call_soon_threadsafe(self.loop.stop) + self.thread.join() + + logger.info(dict(msg='close done')) + + @classmethod + def singleton(cls) -> Self: + if cls._instance is None: + cls._instance = cls() + + return cls._instance + + def pick_option_put_id(self, option_id: int) -> None: + self.loop.call_soon_threadsafe(lambda: self._option_id.set_result(option_id)) + + @dataclasses.dataclass + class entry_t: + path: pathlib.Path + buf_number: Optional[int] = None + + def _get_buffers( + self, + res_future: Optional[asyncio.Future[ + list[entry_t] + ]] = None, + ) -> list[entry_t]: + res = [ + self.entry_t( + buf_number=o.number, + path=pathlib.Path(o.name).absolute(), + ) + for o in vim.buffers + ] + + if res_future: + self.loop.call_soon_threadsafe(lambda: res_future.set_result(res)) + + return res + + async def _switch_buffer(self) -> None: + with self._lock: + self._reset_items() + + await self._sync_task(self._update_items) + + # self._items = buffers + + with self._lock: + self._set_filter_pattern('') + + selected_id = await self._pick_option_from_popup( + # [o[0] for o in buffers] + ) + + logger.info(dict(selected_id=selected_id)) + + def ui_switch_buffer(): + nonlocal selected_id + # nonlocal buffers + + logger.warning(dict( + buffers=self._items[:3], + id=selected_id, + )) + + # print(vim.buffers, selected_id) + + if not selected_id is None: + selected_item = self._items[selected_id] + if selected_item.buf_number is None: + Vim.run_command('badd %s' % json.dumps(str(selected_item.path))[1:-1]) + Vim.run_command('e %s' % json.dumps(str(selected_item.path))[1:-1]) + else: + vim.current.buffer = vim.buffers[selected_item.buf_number] + + with self._lock: + self._queue.append(ui_switch_buffer) + + def switch_buffer(self) -> None: + logger.info(dict(msg='before switch_buffer started')) + result = asyncio.run_coroutine_threadsafe(self._switch_buffer(), self.loop) + + result.add_done_callback(future_dump_exception) + + logger.info(dict(msg='after switch_buffer started')) + + async def _pick_option_from_popup( + self, + # options: list[str], + ) -> Optional[int]: + logger.info(dict(msg='started')) + + self._filter_pattern = '' + self._popup_id = None + + # self._options = options + + self._option_id = asyncio.Future[int]() + + await self._pick_option_start_popup() + + option_id = await self._option_id + + logger.info(dict(option_id=option_id)) + + self._options = None + self._option_id = None + + logger.info(dict(msg='done')) + + if option_id >= 0: + return self._filtered_ids[option_id] + else: + return None + + def ui_thread(self): + with self._lock: + # Vim.run_command(r''' + # set laststatus=2 + # set statusline={} + #'''.format(datetime.datetime.now().isoformat())) + + while len(self._queue) > 0: + cmd = self._queue.pop() + + try: + cmd_str = inspect.getsource(cmd) + except: + cmd_str = str(cmd) + + try: + logger.warning(dict(msg='start command', cmd=cmd_str)) + + cmd() + except: + logger.exception('') + # self._result.append( + # vim.command(cmd) + # ) + + def on_buf_enter(self) -> None: + result = asyncio.run_coroutine_threadsafe( + self._on_buf_enter( + buf_number=vim.current.buffer.number, + buf_name=pathlib.Path(vim.current.buffer.name), + ), + self.loop, + ) + + result.add_done_callback(future_dump_exception) + + def on_filter_key(self, key: str) -> None: + logger.info(dict(msg='got key', key=key)) + + if key == bytes([27]): + logger.info(dict(msg='closing popup')) + + vim.Function('popup_close')(self._popup_id) + return 1 + + if key == b'\x80kb': + logger.info(dict(msg='backspace')) + + with self._lock: + self._set_filter_pattern(self._filter_pattern[:-1]) + + # C-g + elif key == b'\x07': + with self._lock: + self._include_git = not self._include_git + self._update_items() + self._update_filtered() + # self._update_popup() + else: + try: + key_str = key.decode('utf-8') + except: + return vim.Function('popup_filter_menu')( + self._popup_id, key + ) + # return 0 + + if not key_str.isprintable(): + return vim.Function('popup_filter_menu')( + self._popup_id, key + ) + + # return 0 + else: + with self._lock: + self._set_filter_pattern(self._filter_pattern + key_str) + + self._update_popup() + + return 1 + + async def _sync_task( + self, + cb: Callable[[], None], + # future: asyncio.Future[bool] + ) -> None: + res_future: asyncio.Future[bool] = asyncio.Future() + + def wrapper(): + res : bool = True + + try: + cb() + except: + logger.exception('') + res = False + + self.loop.call_soon_threadsafe(lambda: res_future.set_result(res)) + + with self._lock: + self._queue.append(wrapper) + + return await res_future + + def _cwd(cls) -> pathlib.Path: + return pathlib.Path( + vim.Function('getcwd')().decode('utf-8') + ) + + def _update_items(self) -> None: + known_files: dict[str, int] = dict() + + if self._buffers is None: + self._buffers = self._get_buffers() + + logger.info(dict(buffers=self._buffers[:3])) + + if self._include_git: + if self._tracked_files is None: + for o in self._buffers: + assert o.buf_number + + known_files[str(o.path)] = o.buf_number + + ls_files_output = [ + o.strip() + for o in subprocess.check_output( + ['git', 'ls-files', '--recurse-submodules',], cwd=self._cwd(), + ).decode('utf-8').splitlines() + ] + + self._tracked_files = [] + + for o in ls_files_output: + path = pathlib.Path( + o, + ).absolute() + + entry = self.entry_t( + path=path, + buf_number=known_files.get(str(path)), + ) + + if entry.buf_number: + continue + + self._tracked_files.append(entry) + + logger.info(dict(tracked_files=self._tracked_files[:3])) + + self._items = self._buffers + self._tracked_files + else: + self._items = self._buffers + + self._items = sorted( + self._items, + # key=lambda x: -self._buffer_frequency.get(x[1], 0) + key=lambda x: -self._buffer_last_used.get(x.buf_number, 0), + ) + + def _reset_items(self) -> None: + self._buffers = None + self._tracked_files = None + self._items = None + + def _update_filtered(self) -> None: + pattern = re.compile(self._filter_pattern) + + self._filtered_ids = [ + i for i, o in enumerate(self._items) if not pattern.search(str(o.path)) is None + ] + + self._options = [str(self._items[o].path) for o in self._filtered_ids] + + def _set_filter_pattern(self, filter_pattern: str) -> None: + self._filter_pattern = filter_pattern + + self._update_filtered() + + def _update_popup(self) -> None: + vim.Function('popup_settext')( + self._popup_id, + self._options, + ) + vim.Function('popup_setoptions')(self._popup_id, {'title': 'Select a file, [%s]' % self._filter_pattern}) + + async def _on_buf_enter( + self, + buf_number: int, + buf_name: pathlib.Path, + ) -> None: + # logger.info(dict(msg='waiting')) + + with self._lock: + # buf_number = vim.current.buffer.number + + if not buf_number in self._buffer_frequency: + self._buffer_frequency[buf_number] = 0 + + self._buffer_frequency[buf_number] += 1 + + self._buffer_last_used[buf_number] = datetime.datetime.now().timestamp() + + logger.info( + dict( + msg='updated', + buf_path=str(buf_name), + frequency=self._buffer_frequency[buf_number], + buf_number=buf_number, + ) + ) + + async def _pick_option_start_popup( + self, + ): + callback_name = '{}_{}_{}'.format( + MODULE_NAME, + type(self).__name__.lower(), + 'popup_callback', + ).capitalize() + + filter_name = '{}_{}_{}'.format( + MODULE_NAME, + type(self).__name__.lower(), + 'popup_filter', + ).capitalize() + + if int(vim.eval('exists("{}")'.format(callback_name))) == 1: + logger.warning(dict(msg='callback already defined, %s' % callback_name)) + + vim.command( + r""" + function! {callback_name}(id, result) + if a:result > 0 + call py3eval('online_fxreader_pr34_vim.beta.FastSelect.singleton().pick_option_put_id(' . (a:result - 1). ')') + else + call py3eval('online_fxreader_pr34_vim.beta.FastSelect.singleton().pick_option_put_id(-1)') + endif + endfunction + """.format( + callback_name=callback_name, + ) + ) + + vim.command( + r""" + function! {filter_name}(win_id, key) + return py3eval('online_fxreader_pr34_vim.beta.FastSelect.singleton().on_filter_key(key)', #{key: a:key}) + endfunction + """.replace( + '{filter_name}', + filter_name, + ) + ) + + logger.info(dict(msg='before popup')) + + popup_menu = vim.Function('popup_menu') + + def create_popup(): + self._popup_id = popup_menu( + self._options, + { + 'title': 'Select a file', + 'callback': callback_name, + 'filter': filter_name, + 'wrap': 1, + 'maxwidth': 80, + 'close': 'button', + 'resize': 1, + 'drag': 1, + 'maxheight': '16', + }, + ) + + with self._lock: + self._queue.append( + create_popup, + # lambda : vim.command( + # "call popup_menu({options}, {'title': '{title}', 'callback': '{callback}'})".replace( + # '{options}', '[%s]' % ','.join([ + # '\'%s\'' % o.replace('\'', '\\\'') + # for o in self._options + # ]), + # ).replace( + # '{title}', 'Select a file', + # ).replace( + # '{callback}', + # callback_name + # ) + # ) + ) + + # logger.info(dict(popup_id=popup_id)) + + # logger.info(dict(msg='after popup')) + + +def init(): + FastSelect.singleton() diff --git a/dotfiles/.vim/online_fxreader_pr34_vim/main.py b/dotfiles/.vim/online_fxreader_pr34_vim/main.py new file mode 100644 index 0000000..6e07e40 --- /dev/null +++ b/dotfiles/.vim/online_fxreader_pr34_vim/main.py @@ -0,0 +1,265 @@ +import functools +import configparser +import collections +import asyncio +import threading +import re +import inspect +import pathlib +import logging +import fnmatch +import vim + +from typing import ( + Optional, + ClassVar, + Self, + Any, + Callable, +) + +logger = logging.getLogger(__name__) + +from .utils import Vim + +# logging.basicConfig(level=logging.WARNING) + + +MODULE_NAME = 'online_fxreader_pr34_vim' + + +def f1(): + t1 = vim.current.window + t2 = t1.width + vim.command('vnew') + t3 = t2 // 3 + vim.command('vertical resize %d' % t3) + vim.current.window = t1 + + +def f2(): + context = {k: vim.options['splitright'] for k in ['splitright']} + try: + current_window = vim.current.window + vim.options['splitright'] = True + vim.command('vnew') + vim.command('r! tmux show-buffer') + vim.current.window = current_window + finally: + for k, v in context.items(): + vim.options[k] = v + + +def f5_1(pattern, flags, info): + import subprocess + import io + import re + import tempfile + import traceback + import logging + + # print([pattern, flags, info]) + completed_process = None + + options = dict( + recursive=False, + ext=[], + ) + + # print('fuck') + if b'r' in flags: + while True: + ext_m = re.compile(r'^.([^\,]+),(.*)$').match(pattern) + + if pattern[:3] in [r'\r,']: + options['recursive'] = True + pattern = pattern[3:] + elif not ext_m is None: + options['ext'].append(ext_m[1]) + pattern = ext_m[2] + else: + break + + print( + [ + flags, + pattern, + options, + ] + ) + + try: + git_cmd = [ + 'git', + 'grep', + '-n', + ] + + if options['recursive']: + git_cmd.append('--recurse-submodules') + + git_cmd.extend(['-P', pattern]) + + if len(options['ext']) > 0: + git_cmd.extend(['--', *['**/*%s' % o for o in options['ext']]]) + + completed_process = subprocess.run( + git_cmd, + capture_output=True, + ) + assert completed_process.returncode == 0 or ( + completed_process.stdout == b'' + # completed_process.stdout == b'' and + # completed_process.stderr == b'' + ) + t1 = completed_process.stdout + except: + logging.error( + ''.join( + [ + traceback.format_exc(), + getattr(completed_process, 'stdout', b'').decode('utf-8'), + getattr(completed_process, 'stderr', b'').decode('utf-8'), + ] + ) + ) + t1 = b'' + + def watch(data): + with tempfile.NamedTemporaryFile(suffix='.txt') as f: + with io.open(f.name, 'wb') as f2: + f2.write(data) + vim.command('!less %s' % f.name) + + # watch(t1) + + t2 = [] + for o in t1.splitlines(): + try: + # watch(o.encode('utf-8')) + t3 = o.decode('utf-8') + t4 = re.compile(r'^([^\:\=]+)[\:\=](\d+)[\:\=](.*)$').match(t3) + if not t4 is None: + t2.append( + dict( + name=t4[3].strip(), + filename=t4[1], + cmd=t4[2], + ) + ) + except: + pass + # print(t2) + + # return [{'name': 'blah', 'filename': 'docker-compose.yml', 'cmd': '23'}] + return t2 + + +class EditorConfigModeline: + _instance: ClassVar[Optional['EditorConfigModeline']] = None + + def __init__(self) -> None: + self.configs: dict[ + pathlib.Path, + dict[str, str], + ] = dict() + + Vim.run_command(r""" +augroup EditorConfigModeline + autocmd! + " autocmd BufEnter * ++nested python3 import online_fxreader_pr34_vim.main; online_fxreader_pr34_vim.main.EditorConfigModeline.singleton().on_buffer() + autocmd BufWinEnter * ++nested python3 import online_fxreader_pr34_vim.main; online_fxreader_pr34_vim.main.EditorConfigModeline.singleton().on_buffer() +augroup END + """) + + @classmethod + def singleton(cls) -> Self: + if cls._instance is None: + cls._instance = cls() + + return cls._instance + + def load_config(self) -> Optional[dict[str, str]]: + cwd = pathlib.Path.cwd() + + if not cwd in self.configs: + config_path = cwd / '.editorconfig' + + if not config_path.exists(): + return None + + parser = configparser.ConfigParser() + parser.optionxform = str # keep case + try: + parser.read(str(config_path)) + except: + logger.exception('') + return None + + config: dict[str, str] = dict() + + for section in parser.sections(): + logger.info(dict(section=section)) + + if len(section) > 0: + # pattern = section[1:-1] + pattern = section + if not parser[section].get('vim_modeline') is None: + config[pattern] = parser[section].get('vim_modeline') + self.validate_modeline(config[pattern]) + + self.configs[cwd] = config + + return self.configs[cwd] + + @classmethod + def validate_modeline(cls, modeline: str) -> None: + pattern = re.compile(r'^set(\s+(noet|sts|ts|et|ai|ci|noai|noci|sw)(\=\w)?)+$') + assert pattern.match(modeline), 'invalid modeline %s' % modeline + + @classmethod + def find_entry( + cls, + file_path: pathlib.Path, + config: Optional[dict[str, str]] = None, + ) -> Optional[str]: + if config is None: + return None + + project_root = pathlib.Path.cwd() + + if file_path.is_relative_to(project_root): + rel_path = file_path.relative_to(pathlib.Path.cwd()) + else: + rel_path = file_path + + for pattern, modeline in config.items(): + if fnmatch.fnmatch(str(rel_path), pattern): + return modeline + + return None + + def on_buffer(self) -> None: + config = self.load_config() + + logger.info(dict(config=config)) + + buf_name = vim.current.buffer.name + file_path = pathlib.Path(buf_name).resolve() + + entry = self.find_entry(file_path, config=config) + + logger.info(dict(modeline=entry)) + + vim.command('silent! {}'.format(entry)) + + # vim.command("echo '{}'".format('applied %s' % entry)) + + # raise NotImplementedError + + +# EditorConfigModeline.singleton() + + +def init(): + EditorConfigModeline.singleton() diff --git a/dotfiles/.vim/online_fxreader_pr34_vim/utils.py b/dotfiles/.vim/online_fxreader_pr34_vim/utils.py new file mode 100644 index 0000000..02c49f5 --- /dev/null +++ b/dotfiles/.vim/online_fxreader_pr34_vim/utils.py @@ -0,0 +1,17 @@ +import vim +import logging + +logger = logging.getLogger(__name__) + +class Vim: + @classmethod + def run_command(cls, cmd) -> list[str]: + logger.info(dict(cmd=cmd)) + + output: list[str] = [] + for line in cmd.splitlines(): + if line.strip() == '': + continue + output.append(vim.command(line)) + + return output diff --git a/dotfiles/.vimrc b/dotfiles/.vimrc index f820599..8d05fd1 100644 --- a/dotfiles/.vimrc +++ b/dotfiles/.vimrc @@ -7,7 +7,8 @@ if has('python3') source $HOME/.py3.vimrc endif -filetype plugin indent on +" filetype plugin indent on +filetype plugin off set number set noswapfile @@ -30,7 +31,14 @@ colorscheme morning hi MatchParen guifg=white guibg=black gui=NONE ctermfg=1 ctermbg=0 function! MakeSession() - let b:sessiondir = '.vim/' + " let b:sessiondir = '.vim/' + if exists('g:sessiondir') + let b:sessiondir = g:sessiondir + else + let b:sessiondir = getcwd() . '/' . '.vim/' + let g:sessiondir = b:sessiondir + endif + if exists('g:session_name') let b:session_name = g:session_name else @@ -43,11 +51,17 @@ function! MakeSession() endif let b:filename = b:sessiondir . '/' . b:session_name . '.vim' exe "mksession! " . b:filename - echo 'saved ' . b:session_name + echo 'saved ' . b:sessiondir . ' ' . b:session_name endfunction function! LoadSession() - let b:sessiondir = '.vim/' + if exists('g:sessiondir') + let b:sessiondir = g:sessiondir + else + let b:sessiondir = getcwd() . '/' . '.vim/' + let g:sessiondir = b:sessiondir + endif + if exists('g:session_name') let b:session_name = g:session_name else @@ -87,3 +101,5 @@ map i5 :set sw=2 sts=2 ts=2 noet ai ci set foldmethod=indent set nofoldenable map e :e # + +map :python3 import online_fxreader_pr34_vim.beta; online_fxreader_pr34_vim.beta.FastSelect.singleton().switch_buffer() diff --git a/meson/online/fxreader/pr34/commands_typed/archlinux/m.py b/meson/online/fxreader/pr34/commands_typed/archlinux/m.py index afcb078..a5b0c78 100755 --- a/meson/online/fxreader/pr34/commands_typed/archlinux/m.py +++ b/meson/online/fxreader/pr34/commands_typed/archlinux/m.py @@ -86,7 +86,9 @@ class PyProject: third_party_roots: list[ThirdPartyRoot] = dataclasses.field( default_factory=lambda: [], ) - requirements: dict[str, pathlib.Path] = dataclasses.field(default_factory=lambda: dict()) + requirements: dict[str, pathlib.Path] = dataclasses.field( + default_factory=lambda: dict() + ) modules: list[Module] = dataclasses.field( default_factory=lambda: [], @@ -132,7 +134,12 @@ def check_dict( else: VT_class = VT - assert all([isinstance(k, KT) and (VT_class is None or isinstance(v, VT_class)) for k, v in value2.items()]) + assert all( + [ + isinstance(k, KT) and (VT_class is None or isinstance(v, VT_class)) + for k, v in value2.items() + ] + ) if VT is None: return cast( @@ -251,7 +258,12 @@ def pyproject_load( str, ) - if 'tool' in content and isinstance(content['tool'], dict) and tool_name in content['tool'] and isinstance(content['tool'][tool_name], dict): + if ( + 'tool' in content + and isinstance(content['tool'], dict) + and tool_name in content['tool'] + and isinstance(content['tool'][tool_name], dict) + ): pr34_tool = check_dict( check_dict( content['tool'], @@ -264,7 +276,9 @@ def pyproject_load( res.early_features = pr34_tool['early_features'] if 'pip_find_links' in pr34_tool: - res.pip_find_links = [d.parent / pathlib.Path(o) for o in pr34_tool['pip_find_links']] + res.pip_find_links = [ + d.parent / pathlib.Path(o) for o in pr34_tool['pip_find_links'] + ] if 'runtime_libdirs' in pr34_tool: res.runtime_libdirs = [ @@ -283,7 +297,9 @@ def pyproject_load( if 'third_party_roots' in pr34_tool: for o in check_list(pr34_tool['third_party_roots']): o2 = check_dict(o, str, str) - assert all([k in {'package', 'module_root', 'path'} for k in o2]) + assert all( + [k in {'package', 'module_root', 'path'} for k in o2] + ) res.third_party_roots.append( PyProject.ThirdPartyRoot( @@ -297,7 +313,9 @@ def pyproject_load( res.requirements = { k: d.parent / pathlib.Path(v) # pathlib.Path(o) - for k, v in check_dict(pr34_tool['requirements'], str, str).items() + for k, v in check_dict( + pr34_tool['requirements'], str, str + ).items() } if 'modules' in pr34_tool: @@ -364,7 +382,10 @@ class BootstrapSettings: ).strip() ) pip_check_conflicts: Optional[bool] = dataclasses.field( - default_factory=lambda: os.environ.get('PIP_CHECK_CONFLICTS', json.dumps(True)) in [json.dumps(True)], + default_factory=lambda: os.environ.get( + 'PIP_CHECK_CONFLICTS', json.dumps(True) + ) + in [json.dumps(True)], ) uv_cache_dir: str = dataclasses.field( default_factory=lambda: os.environ.get( @@ -438,7 +459,9 @@ def requirements_name_get( else: requirements_path = source_dir / 'requirements.txt' - requirements_path_in = requirements_path.parent / (requirements_path.stem + '.in') + requirements_path_in = requirements_path.parent / ( + requirements_path.stem + '.in' + ) requirements_in: list[str] = [] @@ -594,10 +617,15 @@ def env_bootstrap( requirements_in: list[str] = [] - requirements_in.extend(['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11']) + requirements_in.extend( + ['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11'] + ) if pyproject.early_features: - early_dependencies = sum([pyproject.dependencies[o] for o in pyproject.early_features], cast(list[str], [])) + early_dependencies = sum( + [pyproject.dependencies[o] for o in pyproject.early_features], + cast(list[str], []), + ) logger.info( dict( @@ -647,14 +675,6 @@ def env_bootstrap( needs_compile = True constraint_args = ['-c', str(requirements_path)] - if (not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update) and requirements_path.exists(): - whl_cache_download( - whl_cache_path=bootstrap_settings.whl_cache_path, - requirements_path=requirements_path, - uv_python_version=uv_python_version, - pip_find_links_args=pip_find_links_args, - ) - cache_find_links_args: list[str] = [] if bootstrap_settings.whl_cache_path.exists(): cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] @@ -711,15 +731,15 @@ def env_bootstrap( os.unlink(f_out.name) raise - if not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update: - whl_cache_download( - whl_cache_path=bootstrap_settings.whl_cache_path, - requirements_path=requirements_path, - uv_python_version=uv_python_version, - pip_find_links_args=pip_find_links_args, - ) - if bootstrap_settings.whl_cache_path.exists(): - cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] + if not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update: + whl_cache_download( + whl_cache_path=bootstrap_settings.whl_cache_path, + requirements_path=requirements_path, + uv_python_version=uv_python_version, + pip_find_links_args=pip_find_links_args, + ) + if bootstrap_settings.whl_cache_path.exists(): + cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] if bootstrap_settings.venv_partial and bootstrap_settings.env_path.exists(): logger.info('[bootstrap] VENV_PARTIAL: skipping venv creation (already exists)') diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/etc/systemd/logind.conf b/platform_dotfiles/ideapad_slim_3_15arp10/etc/systemd/logind.conf new file mode 100644 index 0000000..f440a34 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/etc/systemd/logind.conf @@ -0,0 +1,57 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Entries in this file show the compile time defaults. Local configuration +# should be created by either modifying this file (or a copy of it placed in +# /etc/ if the original file is shipped in /usr/), or by creating "drop-ins" in +# the /etc/systemd/logind.conf.d/ directory. The latter is generally +# recommended. Defaults can be restored by simply deleting the main +# configuration file and all drop-ins located in /etc/. +# +# Use 'systemd-analyze cat-config systemd/logind.conf' to display the full config. +# +# See logind.conf(5) for details. + +[Login] +#NAutoVTs=6 +#ReserveVT=6 +#KillUserProcesses=no +#KillOnlyUsers= +#KillExcludeUsers=root +#InhibitDelayMaxSec=5 +#UserStopDelaySec=10 +#HandlePowerKey=hibernate +HandlePowerKey=suspend +#HandlePowerKeyLongPress=ignore +#HandleRebootKey=reboot +#HandleRebootKeyLongPress=poweroff +#HandleSuspendKey=suspend +#HandleSuspendKeyLongPress=hibernate +#HandleHibernateKey=hibernate +#HandleHibernateKeyLongPress=ignore + + +#HandleLidSwitchExternalPower=suspend +#HandleLidSwitchDocked=ignore +#PowerKeyIgnoreInhibited=no +#SuspendKeyIgnoreInhibited=no +#HibernateKeyIgnoreInhibited=no +#LidSwitchIgnoreInhibited=yes +#RebootKeyIgnoreInhibited=no +#HoldoffTimeoutSec=30s +#IdleAction=ignore +#IdleActionSec=30min +#RuntimeDirectorySize=10% +#RuntimeDirectoryInodesMax= +#RemoveIPC=yes +#InhibitorsMax=8192 +#SessionsMax=8192 +#StopIdleSessionSec=infinity + +HandleLidSwitch=suspend +# for sway +#HandleLidSwitch=none diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/10-tun0.rules b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/10-tun0.rules new file mode 100644 index 0000000..b7272d5 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/10-tun0.rules @@ -0,0 +1 @@ +ACTION=="add", SUBSYSTEM=="net", KERNEL=="tun0", TAG+="systemd" diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-leds.rules b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-leds.rules new file mode 100644 index 0000000..f4ffc49 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-leds.rules @@ -0,0 +1,11 @@ +ACTION=="add|change", SUBSYSTEM=="leds", DEVPATH=="/devices/pci0000:00/0000:00:1b.0/hdaudioC0D0/leds/hda::mute", RUN{program}+="/usr/bin/chmod 666 /sys$devpath/brightness" +ACTION=="add|change", SUBSYSTEM=="leds", DEVPATH=="/devices/platform/applesmc.768/leds/smc::kbd_backlight", RUN{program}+="/usr/bin/chmod 666 /sys$devpath/brightness" +# udevadm info --attribute-walk --path=/sys/devices/platform/applesmc.768/ +# udevadm trigger --action=add --verbose --parent-match /devices/platform/applesmc.768/ +#ACTION=="add|change", KERNEL=="applesmc.768", SUBSYSTEM=="platform", DRIVER=="applesmc", RUN{program}+="ls -allh /sys$devpath/", OPTIONS="log_level=debug" +#ACTION=="add|change", KERNEL=="applesmc.768", SUBSYSTEM=="platform", DRIVER=="applesmc", RUN{program}+="/usr/bin/ls -allh /sys$devpath/", OPTIONS="log_level=debug" +ACTION=="add|change", KERNEL=="applesmc.768", SUBSYSTEM=="platform", DRIVER=="applesmc", TAG+="systemd", ENV{SYSTEMD_WANTS}="online.fxreader.pr34.udev@$devnode.service", OPTIONS="log_level=debug" +#KERNEL=="applesmc.768", SUBSYSTEM=="platform", DRIVER=="applesmc", MODE="0660", TAG+="uaccess", OPTIONS="log_level=debug", OPTIONS+="watch" +ACTION=="add|change", DEVPATH=="/class/backlight/intel_backlight", RUN{program}+="/usr/bin/chmod 666 /sys$devpath/brightness" +ACTION=="add|change", KERNEL=="cpu1", SUBSYSTEM=="cpu", TAG+="systemd", ENV{SYSTEMD_WANTS}="online.fxreader.pr34.udev@$devnode.service", OPTIONS="log_level=debug" +#ACTION=="add|change", KERNEL=="cpu[0-9]", SUBSYSTEM=="cpu", TAG+="systemd", ENV{SYSTEMD_WANTS}="online.fxreader.pr34.udev@$devnode.service", OPTIONS="log_level=debug" diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-scsi-power.rules b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-scsi-power.rules new file mode 100644 index 0000000..cbca5a7 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/etc/udev/rules.d/40-scsi-power.rules @@ -0,0 +1 @@ +ACTION=="add", SUBSYSTEM=="scsi_host", KERNEL=="host*", ATTR{link_power_management_policy}="max_performance", OPTIONS="log_level=debug" diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/gnome-shortcuts-macbook-air b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/gnome-shortcuts-macbook-air new file mode 100755 index 0000000..838b40f --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/gnome-shortcuts-macbook-air @@ -0,0 +1,13 @@ +#!/usr/bin/bash + +commands gnome-shortcuts \ + -a \ + 'powersave' \ + 'commands desktop-services --cpufreq-action powersave' \ + '1' + +commands gnome-shortcuts \ + -a \ + 'performance' \ + 'commands desktop-services --cpufreq-action performance' \ + '2' diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/systemd_gtk b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/systemd_gtk new file mode 100755 index 0000000..e564a3a --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/systemd_gtk @@ -0,0 +1,330 @@ +#!/usr/bin/env python3 + +# vi: filetype=python + +import gi +gi.require_version("Gtk", "4.0") +from gi.repository import Gtk, Gio, GLib, GObject + +import subprocess +import shlex +import threading +import uuid +import argparse +import logging + +# CLI / Logging +parser = argparse.ArgumentParser(description="Systemd Scope Manager (GTK4 ColumnView)") +parser.add_argument("--log-level", "-l", choices=["DEBUG","INFO","WARNING","ERROR","CRITICAL"], default="INFO") +parser.add_argument("--values-mode", "-m", choices=["raw","human"], default="human", + help="Display memory / CPU values raw or human‑readable") +args = parser.parse_args() + +logging.basicConfig(level=getattr(logging, args.log_level.upper()), + format="%(asctime)s %(name)s %(levelname)s: %(message)s") +logger = logging.getLogger(__name__) + +# Helpers +def human_bytes(value: int) -> str: + for unit in ("B","KB","MB","GB","TB"): + if value < 1024: + return f"{value:.1f}{unit}" + value /= 1024 + return f"{value:.1f}PB" + +def human_time(nsec: int) -> str: + sec = nsec / 1_000_000_000 + if sec < 60: + return f"{sec:.1f}s" + minutes = sec / 60 + if minutes < 60: + return f"{minutes:.1f}m" + return f"{minutes/60:.1f}h" + +def run_systemctl_show(unit: str) -> dict: + cmd = [ + "systemctl", "--user", "show", unit, + "--property=MemoryCurrent,MemorySwapCurrent,CPUUsageNSec,ActiveState,Restart" + ] + try: + res = subprocess.run(cmd, capture_output=True, text=True, check=True) + props = {} + for line in res.stdout.splitlines(): + if "=" in line: + k, v = line.split("=", 1) + props[k.strip()] = v.strip() + return props + except Exception: + logger.exception("systemctl show failed for %s", unit) + return {} + +# Data row +class ScopeRow(GObject.Object): + __gtype_name__ = "ScopeRow" + unit = GObject.Property(type=str) + cli = GObject.Property(type=str) + mem = GObject.Property(type=str) + swap = GObject.Property(type=str) + cpu = GObject.Property(type=str) + state= GObject.Property(type=str) + + def __init__(self, unit, cli, mem, swap, cpu, state): + super().__init__() + self.unit = unit + self.cli = cli + self.mem = mem + self.swap = swap + self.cpu = cpu + self.state = state + +# Main Window +class ScopeManagerWindow(Gtk.ApplicationWindow): + def __init__(self, app): + super().__init__(application=app, title="Systemd Scope Manager") + self.set_default_size(1000, 500) + self.values_mode = args.values_mode + self.scopes = {} + + self.model = Gio.ListStore(item_type=ScopeRow) + self.sel = Gtk.SingleSelection.new(self.model) + self.view = Gtk.ColumnView.new(self.sel) + self.view.set_reorderable(True) + self.view.set_show_row_separators(True) + + cols = [ + ("Unit", "unit"), + ("CLI", "cli"), + ("Memory", "mem"), + ("Swap", "swap"), + ("CPU", "cpu"), + ("State", "state"), + ] + for title, prop_name in cols: + factory = Gtk.SignalListItemFactory() + factory.connect("setup", self._factory_setup_label) + factory.connect("bind", self._make_factory_bind(prop_name)) + col = Gtk.ColumnViewColumn() + col.set_title(title) + col.set_factory(factory) + col.set_resizable(True) + col.set_expand(True) + self.view.append_column(col) + + # Actions column + action_factory = Gtk.SignalListItemFactory() + action_factory.connect("setup", self._factory_setup_actions) + action_factory.connect("bind", self._factory_bind_actions) + act_col = Gtk.ColumnViewColumn() + act_col.set_title("⋮") + act_col.set_factory(action_factory) + act_col.set_resizable(False) + act_col.set_expand(False) + self.view.append_column(act_col) + + # Input area + self.cmd_entry = Gtk.Entry() + self.cmd_entry.set_placeholder_text("Command to run in new scope") + run_btn = Gtk.Button(label="Run") + run_btn.connect("clicked", self.on_run_clicked) + prop_btn = Gtk.Button(label="Edit Property") + prop_btn.connect("clicked", self.on_edit_property) + + input_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + input_box.append(self.cmd_entry) + input_box.append(run_btn) + input_box.append(prop_btn) + + scrolled = Gtk.ScrolledWindow() + scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled.set_propagate_natural_width(True) + scrolled.set_propagate_natural_height(True) + scrolled.set_child(self.view) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + vbox.set_vexpand(True) + vbox.append(input_box) + vbox.append(scrolled) + self.set_child(vbox) + + GLib.timeout_add_seconds(2, self.refresh_scopes) + + def _factory_setup_label(self, factory, list_item): + lbl = Gtk.Label() + list_item.set_child(lbl) + + def _make_factory_bind(self, prop_name): + def bind(factory, list_item): + row = list_item.get_item() + lbl = list_item.get_child() + lbl.set_text(getattr(row, prop_name)) + row.connect(f"notify::{prop_name}", lambda obj, pspec: lbl.set_text(getattr(obj, prop_name))) + return bind + + def _factory_setup_actions(self, factory, list_item): + btn = Gtk.MenuButton() + btn.set_icon_name("open-menu-symbolic") + # style class if needed: + btn.get_style_context().add_class("flat") + list_item.set_child(btn) + + def _factory_bind_actions(self, factory, list_item): + row = list_item.get_item() + btn = list_item.get_child() + + menu = Gio.Menu() + menu.append("Stop", f"app.stop_{row.unit}") + menu.append("Restart", f"app.restart_{row.unit}") + menu.append("Toggle Auto‑Restart", f"app.toggle_{row.unit}") + + btn.set_menu_model(menu) + + # create actions + self._ensure_row_actions(row) + + def _ensure_row_actions(self, row): + unit = row.unit + # Stop + act_stop = Gio.SimpleAction.new(f"stop_{unit}", None) + act_stop.connect("activate", lambda a, v, r=row: self.menu_action("Stop", r)) + self.add_action(act_stop) + # Restart + act_restart = Gio.SimpleAction.new(f"restart_{unit}", None) + act_restart.connect("activate", lambda a, v, r=row: self.menu_action("Restart", r)) + self.add_action(act_restart) + # Toggle + act_toggle = Gio.SimpleAction.new(f"toggle_{unit}", None) + act_toggle.connect("activate", lambda a, v, r=row: self.menu_action("Toggle Auto‑Restart", r)) + self.add_action(act_toggle) + + def on_run_clicked(self, button): + cmd = self.cmd_entry.get_text().strip() + if not cmd: + return + unit = f"app-{uuid.uuid4().int >> 64}.scope" + argv = ["systemd-run", "--user", "--scope", "--unit", unit, + "-p", "MemoryAccounting=yes", "-p", "CPUAccounting=yes"] + shlex.split(cmd) + logger.info("Starting scope: %s", " ".join(argv)) + + def worker(): + try: + p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + out, err = p.communicate() + if out: + logger.debug("systemd-run stdout [%s]: %s", unit, out.strip()) + if err: + logger.debug("systemd-run stderr [%s]: %s", unit, err.strip()) + except Exception: + logger.exception("Failed to run systemd-run for %s", unit) + + threading.Thread(target=worker, daemon=True).start() + self.scopes[unit] = cmd + self.cmd_entry.set_text("") + + def on_edit_property(self, button): + idx = self.sel.get_selected() + if idx < 0: + return + row = self.model.get_item(idx) + + dialog = Gtk.Dialog(transient_for=self, modal=True, title="Set systemd property") + content = dialog.get_content_area() + grid = Gtk.Grid(row_spacing=6, column_spacing=6, margin=10) + content.append(grid) + + lbl1 = Gtk.Label(label="Property (e.g. MemoryMax):") + entry1 = Gtk.Entry() + lbl2 = Gtk.Label(label="Value (e.g. 512M):") + entry2 = Gtk.Entry() + runtime_chk = Gtk.CheckButton(label="Runtime only") + + grid.attach(lbl1, 0,0,1,1) + grid.attach(entry1, 1,0,1,1) + grid.attach(lbl2, 0,1,1,1) + grid.attach(entry2, 1,1,1,1) + grid.attach(runtime_chk,0,2,2,1) + + dialog.add_button("OK", Gtk.ResponseType.OK) + dialog.add_button("Cancel", Gtk.ResponseType.CANCEL) + dialog.show() + + resp = dialog.run() + if resp == Gtk.ResponseType.OK: + prop = entry1.get_text().strip() + val = entry2.get_text().strip() + rt = runtime_chk.get_active() + dialog.destroy() + self.set_property(row.unit, prop, val, rt) + else: + dialog.destroy() + + def set_property(self, unit, prop, val, runtime_flag): + cmd = ["systemctl", "--user", "set-property"] + if runtime_flag: + cmd.append("--runtime") + cmd += [unit, f"{prop}={val}"] + logger.info("Setting %s=%s on %s (runtime=%s)", prop, val, unit, runtime_flag) + try: + res = subprocess.run(cmd, capture_output=True, text=True) + if res.stdout: + logger.debug("set-property stdout: %s", res.stdout.strip()) + if res.stderr: + logger.debug("set-property stderr: %s", res.stderr.strip()) + except Exception: + logger.exception("Failed set-property for %s", unit) + + def menu_action(self, label, row): + unit = row.unit + try: + if label == "Stop": + subprocess.run(["systemctl","--user","stop",unit]) + elif label == "Restart": + subprocess.run(["systemctl","--user","restart",unit]) + elif label == "Toggle Auto‑Restart": + props = run_systemctl_show(unit) + current = props.get("Restart","no") + new = "no" if current != "no" else "always" + subprocess.run(["systemctl","--user","set-property",unit,f"Restart={new}"]) + except Exception: + logger.exception("Action %s failed on %s", label, unit) + + def refresh_scopes(self): + self.model.remove_all() + for unit, cli in self.scopes.items(): + props = run_systemctl_show(unit) + mem = int(props.get("MemoryCurrent", "0")) + swap = int(props.get("MemorySwapCurrent","0")) + cpu = int(props.get("CPUUsageNSec", "0")) + state = props.get("ActiveState","unknown") + + if self.values_mode == "human": + mem_s = human_bytes(mem) + swap_s = human_bytes(swap) + cpu_s = human_time(cpu) + else: + mem_s = str(mem) + swap_s = str(swap) + cpu_s = str(cpu) + + row = ScopeRow(unit, cli, mem_s, swap_s, cpu_s, state) + self.model.append(row) + + logger.debug("Refreshed %d scopes", self.model.get_n_items()) + return True + +# Application +class ScopeManagerApp(Gtk.Application): + def __init__(self): + super().__init__(application_id="org.systemd.ScopeManager") + self.connect("activate", self.on_activate) + + def on_activate(self, app): + win = ScopeManagerWindow(self) + win.present() + +def main(): + app = ScopeManagerApp() + return app.run() + +if __name__ == "__main__": + import sys + sys.exit(main()) diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online-fxreader-pr34-udev b/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online-fxreader-pr34-udev new file mode 100755 index 0000000..9d426c6 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online-fxreader-pr34-udev @@ -0,0 +1,116 @@ +#!/usr/bin/python3 + +# vi: filetype=python + +import re +import sys +import os +import time +import subprocess +import argparse +import logging + +from typing import (Any,) + +logger = logging.getLogger(__name__) + + +def run() -> None: + logging.basicConfig(level=logging.INFO) + + parser = argparse.ArgumentParser() + parser.add_argument( + '--device', + ) + + options = parser.parse_args() + + DEVICES : dict[str, Any] = dict( + applesmc=dict( + devpath='sys/devices/platform/applesmc.768', + node='/sys/devices/platform/applesmc.768/fan1_manual', + cmd=r''' + chown root:fan /sys/devices/platform/applesmc.768/fan1_* + chmod g+w /sys/devices/platform/applesmc.768/fan1_* + ''', + ), + intel_pstate=dict( + devpath=r'/?sys/devices/system/cpu/cpu0', + node='/sys/devices/system/cpu/intel_pstate/no_turbo', + cmd=r''' + chown root:fan /sys/devices/system/cpu/intel_pstate/no_turbo + chown root:fan /sys/devices/system/cpu/intel_pstate/max_perf_pct + #chown root:fan /sys/devices/system/cpu/intel_pstate/status + chmod g+w /sys/devices/system/cpu/intel_pstate/no_turbo + chmod g+w /sys/devices/system/cpu/intel_pstate/max_perf_pct + #chmod g+w /sys/devices/system/cpu/intel_pstate/status + echo passive > /sys/devices/system/cpu/intel_pstate/status + chown root:fan /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor + chown root:fan /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq + chmod g+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor + chmod g+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq + ''', + ), + amd_pstate=dict( + devpath=r'/?sys/devices/system/cpu/cpu1', + node='/sys/devices/system/cpu/amd_pstate/status', + cmd=r''' + chown root:fan /sys/devices/system/cpu/cpu*/cpufreq/boost + chown root:fan /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor + chown root:fan /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq + chmod g+w /sys/devices/system/cpu/cpu*/cpufreq/boost + chmod g+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor + chmod g+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq + ''', + ), + #governor=dict( + # devpath=r'/?sys/devices/system/cpu/cpu(\d+)', + # node=r'/sys/devices/system/cpu/cpu{0}/cpufreq/scaling_governor', + # cmd=r''' + # chown root:fan /sys/devices/system/cpu/cpu{0}/cpufreq/scaling_governor + # chown root:fan /sys/devices/system/cpu/cpu{0}/cpufreq/scaling_max_freq + # chmod g+w /sys/devices/system/cpu/cpu{0}/cpufreq/scaling_governor + # chmod g+w /sys/devices/system/cpu/cpu{0}/cpufreq/scaling_max_freq + # ''', + #), + ) + + processed : int = 0 + + logger.info(dict(device=options.device)) + + for k, v in DEVICES.items(): + devpath = re.compile(v['devpath']) + + devpath_m = devpath.match(options.device) + + if devpath_m is None: + continue + + node_2 = v['node'].format(*devpath_m.groups()) + + # logger.info(dict(devpath_m=devpath_m, node=node_2)) + + while not os.path.exists(node_2): + #continue + time.sleep(1) + + cmd_2 = v['cmd'].format(*devpath_m.groups()) + + subprocess.check_call(cmd_2, shell=True) + + logger.info(dict( + devpath_m=devpath_m, + node_2=node_2, + cmd_2=cmd_2, + msg='processed', + label=k, + )) + + processed += 1 + + if processed == 0: + raise NotImplementedError + +if __name__ == '__main__': + run() diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online.fxreader.pr34-on-resume b/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online.fxreader.pr34-on-resume new file mode 100755 index 0000000..6a80d36 --- /dev/null +++ b/platform_dotfiles/ideapad_slim_3_15arp10/usr/local/bin/online.fxreader.pr34-on-resume @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +#vi syntax=python +import subprocess +import sys +import logging + +from typing import (Optional,) + +logger = logging.getLogger(__name__) + +logging.basicConfig(level=logging.INFO) + +if sys.argv[1] == 'before-suspend': + logger.info('before-suspend started') + subprocess.check_call(['nmcli', 'radio', 'wifi', 'off']) + #subprocess.check_call(['modprobe', '-r', 'atkbd',]) + subprocess.check_call(['modprobe', '-r', 'ideapad_laptop',]) + subprocess.check_call(['modprobe', '-r', 'i8042',]) + logger.info('before-suspend done') +elif sys.argv[1] == 'after-suspend': + logger.info('after-suspend started') + subprocess.check_call(['modprobe', 'i8042',]) + subprocess.check_call(['modprobe', 'ideapad_laptop',]) + #subprocess.check_call(['modprobe', 'atkbd',]) + subprocess.check_call(['nmcli', 'radio', 'wifi', 'on']) + #subprocess.check_call(['rfkill', 'unblock', '109']) + #subprocess.check_call(r''' + # # systemctl restart wg-quick@siarhei-hp.service + #''', shell=True,) + logger.info('after-suspend done') +elif sys.argv[1] == 'lid-switch': + import evdev + import time + import io + + lid = evdev.UInput({5 : [0]}, name="virtual-lid-switch") + + last_state : Optional[bool] = None + + try: + while True: + try: + with io.open('/proc/acpi/button/lid/LID0/state', 'r') as f: + value = f.read() + except: + logger.exception('') + value = None + time.sleep(1) + continue + + if not value is None: + if 'open' in value: + is_opened = True + else: + is_opened = False + + if last_state != is_opened: + if is_opened: + logger.info(dict(msg='lid opened')) + lid.write(5, 0, 0) + lid.write(0, 0, 0) + else: + logger.info(dict(msg='lid closed')) + lid.write(5, 0, 1) + lid.write(0, 0, 0) + + last_state = is_opened + + time.sleep(0.1) + finally: + lid.close() +else: + raise NotImplementedError diff --git a/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:48:40+03:00.gpg b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:48:40+03:00.gpg new file mode 100644 index 0000000..3b87013 --- /dev/null +++ b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:48:40+03:00.gpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:669462268da0a51921d882ff5869072e4a04723a0002112bf16bd2e9f387ed4c +size 1257 diff --git a/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:50:49+03:00.gpg b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:50:49+03:00.gpg new file mode 100644 index 0000000..c7f78dc --- /dev/null +++ b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-11-30T21:50:49+03:00.gpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e8a26795fe289518464fa5de23898531af3dcb0a06d292c7bbd3fb86a473a1d +size 1666 diff --git a/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T10:10:51+03:00.gpg b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T10:10:51+03:00.gpg new file mode 100644 index 0000000..fc35651 --- /dev/null +++ b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T10:10:51+03:00.gpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6d9e5fd66267dada45cef3617136fb8bef43f61afc3bacf18ff7d65a5c64137 +size 19015 diff --git a/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T11:59:04+03:00.gpg b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T11:59:04+03:00.gpg new file mode 100644 index 0000000..881d542 --- /dev/null +++ b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-04T11:59:04+03:00.gpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:884b1c2c0476faba695df7e90c0bd7ef6cb9241ea0e7f88bc73e046486cd142e +size 18999 diff --git a/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-13T19:15:17+03:00.gpg b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-13T19:15:17+03:00.gpg new file mode 100644 index 0000000..1eeea44 --- /dev/null +++ b/platform_dotfiles_gpg/ideapad_slim_3_15arp10/sensitive-configs-2025-12-13T19:15:17+03:00.gpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03b5120f4ac2433e59a9e146a3983ba76c097cb32958684e482b66dd7183d9bf +size 19038 diff --git a/python/_m.py b/python/_m.py index b63426e..7f327ab 100644 --- a/python/_m.py +++ b/python/_m.py @@ -53,7 +53,10 @@ def js(argv: list[str]) -> int: '--project-directory', Settings.settings().project_root, '-f', - Settings.settings().project_root / 'docker' / 'js' / 'docker-compose.yml', + Settings.settings().project_root + / 'docker' + / 'js' + / 'docker-compose.yml', *argv, ] ) @@ -67,7 +70,15 @@ def env( env_path = Settings.settings().env_path if not env_path.exists(): - subprocess.check_call([sys.executable, '-m', 'venv', '--system-site-packages', str(env_path)]) + subprocess.check_call( + [ + sys.executable, + '-m', + 'venv', + '--system-site-packages', + str(env_path), + ] + ) subprocess.check_call( [ @@ -233,7 +244,9 @@ Command: TypeAlias = Literal[ def run(argv: Optional[list[str]] = None) -> None: logging.basicConfig( level=logging.INFO, - format=('%(levelname)s:%(name)s:%(message)s:%(process)d:%(asctime)s:%(pathname)s:%(funcName)s:%(lineno)s'), + format=( + '%(levelname)s:%(name)s:%(message)s:%(process)d:%(asctime)s:%(pathname)s:%(funcName)s:%(lineno)s' + ), ) if argv is None: diff --git a/python/cli.py b/python/cli.py index 2311829..567e3ac 100644 --- a/python/cli.py +++ b/python/cli.py @@ -58,8 +58,18 @@ class CLI(_cli.CLI): self._projects: dict[str, _cli.Project] = { 'online.fxreader.pr34': _cli.Project( source_dir=self.settings.base_dir / 'python', - build_dir=self.settings.base_dir / 'tmp' / 'online' / 'fxreader' / 'pr34' / 'build', - dest_dir=self.settings.base_dir / 'tmp' / 'online' / 'fxreader' / 'pr34' / 'install', + build_dir=self.settings.base_dir + / 'tmp' + / 'online' + / 'fxreader' + / 'pr34' + / 'build', + dest_dir=self.settings.base_dir + / 'tmp' + / 'online' + / 'fxreader' + / 'pr34' + / 'install', meson_path=self.settings.base_dir / 'python' / 'meson.build', ), 'online.fxreader.pr34.commands_typed.archlinux': _cli.Project( @@ -125,7 +135,9 @@ class CLI(_cli.CLI): parser = argparse.ArgumentParser() parser.add_argument('command', choices=[o.value for o in Command]) - parser.add_argument('-p', '--project', choices=[o for o in self.projects]) + parser.add_argument( + '-p', '--project', choices=[o for o in self.projects] + ) parser.add_argument( '-o', '--output_dir', diff --git a/python/deps/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl b/python/deps/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl new file mode 100644 index 0000000..6de61f6 --- /dev/null +++ b/python/deps/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b73f4caecb1ce6b94b4b3a87299e779b843ceb8d7a581da8d27abe622a6a37db +size 76119 diff --git a/python/m.py b/python/m.py index 6430da2..502200f 100755 --- a/python/m.py +++ b/python/m.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 import glob +import importlib +import json import io import tempfile import dataclasses @@ -8,13 +10,18 @@ import sys import subprocess import os import logging - - +import re import typing + from typing import ( Optional, Any, + cast, + Type, + TypeVar, + Callable, + overload, ) if typing.TYPE_CHECKING: @@ -23,22 +30,29 @@ if typing.TYPE_CHECKING: BinaryIO, ) + logger = logging.getLogger(__name__) def toml_load(f: 'BinaryIO') -> Any: try: - import tomllib + tomllib = importlib.import_module('tomllib') - return tomllib.load(f) - except: + return cast( + Callable[[Any], Any], + getattr( + tomllib, + 'load', + ), + )(f) + except ModuleNotFoundError: pass try: import tomli return tomli.load(f) - except: + except ModuleNotFoundError: pass raise NotImplementedError @@ -46,13 +60,146 @@ def toml_load(f: 'BinaryIO') -> Any: @dataclasses.dataclass class PyProject: + @dataclasses.dataclass + class Module: + name: str + meson: Optional[pathlib.Path] = None + tool: dict[str, Any] = dataclasses.field(default_factory=lambda: dict()) + scripts: dict[str, str] = dataclasses.field(default_factory=lambda: dict()) + project: dict[str, Any] = dataclasses.field(default_factory=lambda: dict()) + path: pathlib.Path dependencies: dict[str, list[str]] + name: Optional[str] = None + version: Optional[str] = None early_features: Optional[list[str]] = None pip_find_links: Optional[list[pathlib.Path]] = None runtime_libdirs: Optional[list[pathlib.Path]] = None runtime_preload: Optional[list[pathlib.Path]] = None - requirements: dict[str, pathlib.Path] = dataclasses.field(default_factory=lambda: dict()) + + @dataclasses.dataclass + class ThirdPartyRoot: + package: Optional[str] = None + module_root: Optional[str] = None + path: Optional[str] = None + + third_party_roots: list[ThirdPartyRoot] = dataclasses.field( + default_factory=lambda: [], + ) + requirements: dict[str, pathlib.Path] = dataclasses.field( + default_factory=lambda: dict() + ) + + modules: list[Module] = dataclasses.field( + default_factory=lambda: [], + ) + + tool: dict[str, Any] = dataclasses.field( + default_factory=lambda: dict(), + ) + + +Key = TypeVar('Key') +Value = TypeVar('Value') + + +@overload +def check_dict( + value: Any, + KT: Type[Key], + VT: Type[Value], +) -> dict[Key, Value]: ... + + +@overload +def check_dict( + value: Any, + KT: Type[Key], +) -> dict[Key, Any]: ... + + +def check_dict( + value: Any, + KT: Type[Key], + VT: Optional[Type[Value]] = None, +) -> dict[Key, Value]: + assert isinstance(value, dict) + value2 = cast(dict[Any, Any], value) + + VT_class: Optional[type[Any]] = None + + if not VT is None: + if not typing.get_origin(VT) is None: + VT_class = cast(type[Any], typing.get_origin(VT)) + else: + VT_class = VT + + assert all( + [ + isinstance(k, KT) and (VT_class is None or isinstance(v, VT_class)) + for k, v in value2.items() + ] + ) + + if VT is None: + return cast( + dict[Key, Any], + value, + ) + else: + return cast( + dict[Key, Value], + value, + ) + + +@overload +def check_list( + value: Any, + VT: Type[Value], +) -> list[Value]: ... + + +@overload +def check_list( + value: Any, +) -> list[Any]: ... + + +def check_list( + value: Any, + VT: Optional[Type[Value]] = None, +) -> list[Value] | list[Any]: + assert isinstance(value, list) + value2 = cast(list[Any], value) + + assert all([(VT is None or isinstance(o, VT)) for o in value2]) + + if VT is None: + return cast( + list[Any], + value, + ) + else: + return cast( + list[Value], + value, + ) + + +def check_type( + value: Any, + VT: Type[Value], + attribute_name: Optional[str] = None, +) -> Value: + if attribute_name: + attribute_value = getattr(value, attribute_name) + assert isinstance(attribute_value, VT) + return attribute_value + else: + assert isinstance(value, VT) + + return value def pyproject_load( @@ -70,55 +217,158 @@ def pyproject_load( if 'optional-dependencies' in content['project']: assert isinstance(content['project']['optional-dependencies'], dict) - for k, v in content['project']['optional-dependencies'].items(): - assert isinstance(v, list) - assert isinstance(k, str) + for k, v in check_dict( + check_dict( + check_dict( + content, + str, + # Any, + )['project'], + str, + # Any, + )['optional-dependencies'], + str, + list[Any], + ).items(): + # assert isinstance(v, list) + # assert isinstance(k, str) dependencies[k] = v + name: Optional[str] = None + if 'name' in content.get('project', {}): + name = content['project']['name'] + + version: Optional[str] = None + if 'version' in content.get('project', {}): + version = content['project']['version'] + res = PyProject( path=d, dependencies=dependencies, + name=name, + version=version, ) tool_name = 'online.fxreader.pr34'.replace('.', '-') - if 'tool' in content and isinstance(content['tool'], dict) and tool_name in content['tool'] and isinstance(content['tool'][tool_name], dict): - if 'early_features' in content['tool'][tool_name]: - res.early_features = content['tool'][tool_name]['early_features'] + if 'tool' in content: + res.tool = check_dict( + content['tool'], + str, + ) - if 'pip_find_links' in content['tool'][tool_name]: - res.pip_find_links = [d.parent / pathlib.Path(o) for o in content['tool'][tool_name]['pip_find_links']] + if ( + 'tool' in content + and isinstance(content['tool'], dict) + and tool_name in content['tool'] + and isinstance(content['tool'][tool_name], dict) + ): + pr34_tool = check_dict( + check_dict( + content['tool'], + str, + )[tool_name], + str, + ) - if 'runtime_libdirs' in content['tool'][tool_name]: + if 'early_features' in pr34_tool: + res.early_features = pr34_tool['early_features'] + + if 'pip_find_links' in pr34_tool: + res.pip_find_links = [ + d.parent / pathlib.Path(o) for o in pr34_tool['pip_find_links'] + ] + + if 'runtime_libdirs' in pr34_tool: res.runtime_libdirs = [ d.parent / pathlib.Path(o) # pathlib.Path(o) - for o in content['tool'][tool_name]['runtime_libdirs'] + for o in check_list(pr34_tool['runtime_libdirs'], str) ] - if 'runtime_preload' in content['tool'][tool_name]: + if 'runtime_preload' in pr34_tool: res.runtime_preload = [ d.parent / pathlib.Path(o) # pathlib.Path(o) - for o in content['tool'][tool_name]['runtime_preload'] + for o in check_list(pr34_tool['runtime_preload'], str) ] - if 'requirements' in content['tool'][tool_name]: - assert isinstance(content['tool'][tool_name]['requirements'], dict) + if 'third_party_roots' in pr34_tool: + for o in check_list(pr34_tool['third_party_roots']): + o2 = check_dict(o, str, str) + assert all( + [k in {'package', 'module_root', 'path'} for k in o2] + ) + res.third_party_roots.append( + PyProject.ThirdPartyRoot( + package=o2.get('package'), + module_root=o2.get('module_root'), + path=o2.get('path'), + ) + ) + + if 'requirements' in pr34_tool: res.requirements = { k: d.parent / pathlib.Path(v) # pathlib.Path(o) - for k, v in content['tool'][tool_name]['requirements'].items() + for k, v in check_dict( + pr34_tool['requirements'], str, str + ).items() } + if 'modules' in pr34_tool: + modules = check_list(pr34_tool['modules']) + # res.modules = [] + + for o in modules: + assert isinstance(o, dict) + assert 'name' in o and isinstance(o['name'], str) + + module = PyProject.Module( + name=o['name'], + ) + + if 'meson' in o: + assert 'meson' in o and isinstance(o['meson'], str) + + module.meson = pathlib.Path(o['meson']) + + if 'tool' in o: + module.tool.update( + check_dict( + o['tool'], + str, + ) + ) + + if 'scripts' in o: + module.scripts.update( + check_dict( + o['scripts'], + str, + str, + ) + ) + + if 'project' in o: + module.project.update( + check_dict( + o['project'], + str, + ) + ) + + res.modules.append(module) + return res @dataclasses.dataclass class BootstrapSettings: env_path: pathlib.Path + whl_cache_path: pathlib.Path python_path: pathlib.Path base_dir: pathlib.Path python_version: Optional[str] = dataclasses.field( @@ -131,12 +381,27 @@ class BootstrapSettings: ), ).strip() ) + pip_check_conflicts: Optional[bool] = dataclasses.field( + default_factory=lambda: os.environ.get( + 'PIP_CHECK_CONFLICTS', json.dumps(True) + ) + in [json.dumps(True)], + ) + uv_cache_dir: str = dataclasses.field( + default_factory=lambda: os.environ.get( + 'UV_CACHE_DIR', + str(pathlib.Path.cwd() / '.uv-cache'), + ) + ) uv_args: list[str] = dataclasses.field( default_factory=lambda: os.environ.get( 'UV_ARGS', - '--offline', + '--no-index -U', ).split(), ) + whl_cache_update: Optional[bool] = dataclasses.field(default_factory=lambda: os.environ.get('WHL_CACHE_UPDATE', json.dumps(False)) in [json.dumps(True)]) + uv_compile_allow_index: bool = dataclasses.field(default_factory=lambda: os.environ.get('UV_COMPILE_ALLOW_INDEX', json.dumps(False)) in [json.dumps(True)]) + venv_partial: bool = dataclasses.field(default_factory=lambda: os.environ.get('VENV_PARTIAL', json.dumps(False)) in [json.dumps(True)]) @classmethod def get( @@ -146,20 +411,181 @@ class BootstrapSettings: if base_dir is None: base_dir = pathlib.Path.cwd() - env_path = base_dir / '.venv' + env_path: Optional[pathlib.Path] = None + if 'ENV_PATH' in os.environ: + env_path = pathlib.Path(os.environ['ENV_PATH']) + else: + env_path = base_dir / '.venv' + + whl_cache_path = env_path.parent / '.venv-whl-cache' + python_path = env_path / 'bin' / 'python3' return cls( base_dir=base_dir, env_path=env_path, + whl_cache_path=whl_cache_path, python_path=python_path, ) +class requirements_name_get_t: + @dataclasses.dataclass + class res_t: + not_compiled: pathlib.Path + compiled: pathlib.Path + name: str + + +def requirements_name_get( + source_dir: pathlib.Path, + python_version: Optional[str], + features: list[str], + requirements: dict[str, pathlib.Path], +) -> requirements_name_get_t.res_t: + requirements_python_version: Optional[str] = None + if not python_version is None: + requirements_python_version = python_version.replace('.', '_') + + requirements_name = '_'.join(sorted(features)) + + if requirements_python_version: + requirements_name += '_' + requirements_python_version + + requirements_path: Optional[pathlib.Path] = None + + if requirements_name in requirements: + requirements_path = requirements[requirements_name] + else: + requirements_path = source_dir / 'requirements.txt' + + requirements_path_in = requirements_path.parent / ( + requirements_path.stem + '.in' + ) + + requirements_in: list[str] = [] + + return requirements_name_get_t.res_t( + not_compiled=requirements_path_in, + compiled=requirements_path, + name=requirements_name, + ) + + +class packaging_t: + class constants_t: + canonicalize_re: typing.ClassVar[re.Pattern[str]] = re.compile(r'[-_.]+') + req_spec_re: typing.ClassVar[re.Pattern[str]] = re.compile(r'^([a-zA-Z0-9._-]+)==([^\s;]+)') + + @dataclasses.dataclass + class pkg_id_t: + name: str + version: str + + @staticmethod + def canonicalize_name(name: str) -> str: + return packaging_t.constants_t.canonicalize_re.sub('-', name).lower() + + @staticmethod + def parse_whl_name_version(filename: str) -> Optional['packaging_t.pkg_id_t']: + parts = filename.split('-') + if len(parts) >= 3 and filename.endswith('.whl'): + return packaging_t.pkg_id_t( + name=packaging_t.canonicalize_name(parts[0]), + version=parts[1], + ) + return None + + @staticmethod + def parse_req_spec(line: str) -> Optional['packaging_t.pkg_id_t']: + m = packaging_t.constants_t.req_spec_re.match(line) + if m: + return packaging_t.pkg_id_t( + name=packaging_t.canonicalize_name(m.group(1)), + version=m.group(2), + ) + return None + + +def whl_cache_download( + whl_cache_path: pathlib.Path, + requirements_path: pathlib.Path, + uv_python_version: list[str], + pip_find_links_args: list[str], +) -> None: + whl_cache_path.mkdir(parents=True, exist_ok=True) + + cached_pkgs: set[tuple[str, str]] = set() + for whl in whl_cache_path.glob('*.whl'): + parsed = packaging_t.parse_whl_name_version(whl.name) + if parsed is not None: + cached_pkgs.add((parsed.name, parsed.version)) + + missing_reqs: list[str] = [] + with io.open(requirements_path, 'r') as f: + for line in f: + stripped = line.strip() + if not stripped or stripped.startswith('#') or stripped.startswith('--hash'): + continue + spec = stripped.rstrip(' \\') + if spec.startswith('#'): + continue + parsed = packaging_t.parse_req_spec(spec) + if parsed is not None and (parsed.name, parsed.version) in cached_pkgs: + logger.info(dict(msg='cached', pkg='%s==%s' % (parsed.name, parsed.version))) + continue + missing_reqs.append(spec) + + if not missing_reqs: + logger.info(dict(msg='all wheels cached, skipping pip download')) + return + + logger.info(dict(msg='downloading missing wheels', count=len(missing_reqs), pkgs=missing_reqs)) + + with tempfile.NamedTemporaryFile(mode='w', prefix='requirements_missing_', suffix='.txt', delete=False) as f: + f.write('\n'.join(missing_reqs)) + f.flush() + missing_req_path = f.name + + try: + cmd = [ + sys.executable, + '-m', + 'pip', + 'download', + '--only-binary=:all:', + *uv_python_version, + *pip_find_links_args, + '-r', + missing_req_path, + '-d', + str(whl_cache_path), + ] + logger.info(dict(cmd=cmd)) + subprocess.check_call(cmd) + finally: + os.unlink(missing_req_path) + + +def check_host_prerequisites() -> None: + for mod in ['pip', 'uv']: + try: + subprocess.check_call( + [sys.executable, '-m', mod, '--version'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + logger.error('[bootstrap] %s -m %s is not available on the host system' % (sys.executable, mod)) + sys.exit(1) + + def env_bootstrap( bootstrap_settings: BootstrapSettings, pyproject: PyProject, ) -> None: + check_host_prerequisites() + pip_find_links: list[pathlib.Path] = [] if not pyproject.pip_find_links is None: @@ -173,7 +599,7 @@ def env_bootstrap( ] for o in pip_find_links ], - [], + cast(list[str], []), ) features: list[str] = [] @@ -181,31 +607,29 @@ def env_bootstrap( if pyproject.early_features: features.extend(pyproject.early_features) - requirements_python_version: Optional[str] = None - if not bootstrap_settings.python_version is None: - requirements_python_version = bootstrap_settings.python_version.replace('.', '_') - - requirements_name = '_'.join(sorted(features)) - - if requirements_python_version: - requirements_name += '_' + requirements_python_version - - requirements_path: Optional[pathlib.Path] = None - - if requirements_name in pyproject.requirements: - requirements_path = pyproject.requirements[requirements_name] - else: - requirements_path = pyproject.path.parent / 'requirements.txt' + requirements_name_get_res = requirements_name_get( + python_version=bootstrap_settings.python_version, + features=features, + requirements=pyproject.requirements, + source_dir=pyproject.path.parent, + ) + requirements_path = requirements_name_get_res.compiled requirements_in: list[str] = [] - requirements_in.extend(['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11']) + requirements_in.extend( + ['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11'] + ) if pyproject.early_features: - early_dependencies = sum([pyproject.dependencies[o] for o in pyproject.early_features], []) + early_dependencies = sum( + [pyproject.dependencies[o] for o in pyproject.early_features], + cast(list[str], []), + ) logger.info( dict( + requirements_name_get_res=requirements_name_get_res, early_dependencies=early_dependencies, ) ) @@ -222,67 +646,144 @@ def env_bootstrap( # *early_dependencies, # ]) - if not requirements_path.exists(): - with tempfile.NamedTemporaryFile( - mode='w', - prefix='requirements', - suffix='.in', - ) as f: - f.write('\n'.join(requirements_in)) - f.flush() - - subprocess.check_call( - [ - 'uv', - 'pip', - 'compile', - '--generate-hashes', - *pip_find_links_args, - # '-p', - # bootstrap_settings.python_path, - *bootstrap_settings.uv_args, - '-o', - str(requirements_path), - f.name, - ] - ) - uv_python_version: list[str] = [] + venv_python_version: list[str] = [] if not bootstrap_settings.python_version is None: uv_python_version.extend( + [ + # '-p', + '--python-version', + bootstrap_settings.python_version, + ] + ) + venv_python_version.extend( [ '-p', + # '--python-version', bootstrap_settings.python_version, ] ) - subprocess.check_call( - [ - 'uv', - 'venv', - *uv_python_version, - *pip_find_links_args, - # '--seed', - *bootstrap_settings.uv_args, - str(bootstrap_settings.env_path), - ] - ) + logger.info('[bootstrap] step 1/5: compile requirements') - subprocess.check_call( - [ - 'uv', - 'pip', - 'install', - *pip_find_links_args, - '-p', - bootstrap_settings.python_path, - '--require-hashes', - *bootstrap_settings.uv_args, - '-r', - str(requirements_path), - ] - ) + needs_compile = not requirements_path.exists() + constraint_args: list[str] = [] + + if bootstrap_settings.venv_partial and requirements_path.exists(): + logger.info('[bootstrap] VENV_PARTIAL: recompiling with existing requirements.txt as constraints') + needs_compile = True + constraint_args = ['-c', str(requirements_path)] + + cache_find_links_args: list[str] = [] + if bootstrap_settings.whl_cache_path.exists(): + cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] + + if needs_compile: + with ( + tempfile.NamedTemporaryFile( + mode='w', + prefix='requirements', + suffix='.in', + ) as f_in, + tempfile.NamedTemporaryFile( + mode='w', + prefix='requirements', + suffix='.txt', + dir=requirements_path.parent, + delete=False, + ) as f_out, + ): + f_in.write('\n'.join(requirements_in)) + f_in.flush() + + uv_compile_args = bootstrap_settings.uv_args + if bootstrap_settings.uv_compile_allow_index: + uv_compile_args = [o for o in uv_compile_args if o not in ('--no-index', '-U', '--upgrade')] + + if len(constraint_args) > 0: + uv_compile_args = [o for o in uv_compile_args if o not in ('-U', '--upgrade')] + + cmd = [ + 'uv', + '--cache-dir', + bootstrap_settings.uv_cache_dir, + 'pip', + 'compile', + *uv_python_version, + '--generate-hashes', + '--no-annotate', + '--no-header', + *pip_find_links_args, + *cache_find_links_args, + *constraint_args, + *uv_compile_args, + '-o', + f_out.name, + f_in.name, + ] + logger.info(dict(cmd=cmd)) + + try: + subprocess.check_call(cmd) + os.replace(f_out.name, str(requirements_path)) + except subprocess.CalledProcessError: + os.unlink(f_out.name) + raise + + if not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update: + whl_cache_download( + whl_cache_path=bootstrap_settings.whl_cache_path, + requirements_path=requirements_path, + uv_python_version=uv_python_version, + pip_find_links_args=pip_find_links_args, + ) + if bootstrap_settings.whl_cache_path.exists(): + cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] + + if bootstrap_settings.venv_partial and bootstrap_settings.env_path.exists(): + logger.info('[bootstrap] VENV_PARTIAL: skipping venv creation (already exists)') + else: + subprocess.check_call( + [ + 'uv', + '--cache-dir', + bootstrap_settings.uv_cache_dir, + *[o for o in bootstrap_settings.uv_args if o not in ['-U', '--upgrade', '--no-index']], + 'venv', + *venv_python_version, + *cache_find_links_args, + str(bootstrap_settings.env_path), + ] + ) + + cmd = [ + 'uv', + '--cache-dir', + bootstrap_settings.uv_cache_dir, + 'pip', + 'install', + *uv_python_version, + *cache_find_links_args, + '-p', + str(bootstrap_settings.python_path), + '--require-hashes', + *bootstrap_settings.uv_args, + '-r', + str(requirements_path), + ] + logger.info(dict(cmd=cmd)) + subprocess.check_call(cmd) + + if bootstrap_settings.pip_check_conflicts: + subprocess.check_call( + [ + bootstrap_settings.python_path, + '-m', + 'online.fxreader.pr34.commands', + 'pip_check_conflicts', + ] + ) def paths_equal(a: pathlib.Path | str, b: pathlib.Path | str) -> bool: @@ -303,9 +804,12 @@ def run( pyproject: PyProject = pyproject_load(d) - logging.basicConfig(level=logging.INFO) + logging.basicConfig( + level=logging.INFO, + format='%(levelname)s:%(name)s:%(message)s:%(process)d:%(asctime)s:%(pathname)s:%(funcName)s:%(lineno)s', + ) - if not bootstrap_settings.env_path.exists(): + if not bootstrap_settings.env_path.exists() or bootstrap_settings.venv_partial: env_bootstrap( bootstrap_settings=bootstrap_settings, pyproject=pyproject, @@ -334,6 +838,6 @@ def run( if __name__ == '__main__': run( - d=pathlib.Path(__file__).parent / 'pyproject.toml', + d=pathlib.Path(__file__).parent / 'pyproject.common.toml', cli_path=pathlib.Path(__file__).parent / 'cli.py', ) diff --git a/python/meson.build b/python/meson.build index 73e8419..8a819c2 100644 --- a/python/meson.build +++ b/python/meson.build @@ -5,7 +5,7 @@ project( ).stdout().strip('\n'), # 'online.fxreader.uv', # ['c', 'cpp'], - version: '0.1.5.17+27.23', + version: '0.1.5.65', # default_options: [ # 'cpp_std=c++23', # # 'prefer_static=true', diff --git a/python/online/fxreader/pr34/commands.py b/python/online/fxreader/pr34/commands.py index 8753e6a..6e20f27 100644 --- a/python/online/fxreader/pr34/commands.py +++ b/python/online/fxreader/pr34/commands.py @@ -60,7 +60,10 @@ def custom_notify( if sys.platform == 'darwin': osascript_translate = functools.partial( custom_translate, - check=lambda a, b: not re.compile(r'^[a-zA-Z0-9\<\>\/\(\)\s\.\,\:]*$').match(b) is None, + check=lambda a, b: not re.compile( + r'^[a-zA-Z0-9\<\>\/\(\)\s\.\,\:]*$' + ).match(b) + is None, ) subprocess.check_call( @@ -75,7 +78,9 @@ def custom_notify( ] ) else: - subprocess.check_call(['notify-send', '-t', '%d' % timeout2, title, msg[-128:]]) + subprocess.check_call( + ['notify-send', '-t', '%d' % timeout2, title, msg[-128:]] + ) class intercept_output_t: @@ -130,7 +135,11 @@ def intercept_output( last_data = None while not (not current_subprocess.poll() is None and not last_data is None): - if not timeout is None and (datetime.datetime.now() - start_timestamp).total_seconds() > timeout: + if ( + not timeout is None + and (datetime.datetime.now() - start_timestamp).total_seconds() + > timeout + ): break t2 = t1.poll(100) @@ -199,7 +208,12 @@ def intercept_output( def player_metadata() -> Optional[str]: for k in range(20): try: - metadata = {k: subprocess.check_output(['playerctl', 'metadata', k]).decode('utf-8').strip() for k in ['artist', 'title']} + metadata = { + k: subprocess.check_output(['playerctl', 'metadata', k]) + .decode('utf-8') + .strip() + for k in ['artist', 'title'] + } return '%s - %s' % (metadata['artist'], metadata['title']) time.sleep(1.0) except Exception: @@ -219,7 +233,9 @@ def memory_stats() -> memory_stats_t.res_t: with io.BytesIO(subprocess.check_output('free', shell=True)) as f: t1 = f.read().decode('utf-8').splitlines() mem_total = int(t1[1].strip().split()[1]) - mem_used = int(t1[1].strip().split()[2]) + int(t1[1].strip().split()[4]) + mem_used = int(t1[1].strip().split()[2]) + int( + t1[1].strip().split()[4] + ) return dict( mem_total=mem_total, @@ -241,7 +257,9 @@ def memory_stats() -> memory_stats_t.res_t: t1 = subprocess.check_output('vm_stat').decode('utf-8') t2 = [o.split(':') for o in t1.splitlines() if ':' in o] t3 = { - o[0].replace(' ', '_').replace('-', '_').lower(): int(o[1].strip().rstrip('.')) + o[0].replace(' ', '_').replace('-', '_').lower(): int( + o[1].strip().rstrip('.') + ) for o in t2 if len(o) == 2 and len(o[0]) > 0 @@ -382,7 +400,9 @@ def eternal_oom(argv: list[str]) -> None: p = config[app] try: - t1 = subprocess.check_output(['pgrep', '-a', '-f', p[0]]).decode('utf-8') + t1 = subprocess.check_output( + ['pgrep', '-a', '-f', p[0]] + ).decode('utf-8') except Exception: continue t2 = t1.splitlines() @@ -429,9 +449,17 @@ def eternal_oom(argv: list[str]) -> None: if isinstance(options.memory_limit, float): options.memory_limit = int(options.memory_limit) - assert isinstance(options.memory_limit, int) and options.memory_limit < memory_stats()['mem_total'] * 0.95 and options.memory_limit > 512 * 1024 + assert ( + isinstance(options.memory_limit, int) + and options.memory_limit < memory_stats()['mem_total'] * 0.95 + and options.memory_limit > 512 * 1024 + ) - assert isinstance(options.cpu_limit, float) and options.cpu_limit > 0.2 * cpu_count and options.cpu_limit < cpu_count * 0.95 + assert ( + isinstance(options.cpu_limit, float) + and options.cpu_limit > 0.2 * cpu_count + and options.cpu_limit < cpu_count * 0.95 + ) assert options.period >= 1 @@ -459,7 +487,10 @@ def eternal_oom(argv: list[str]) -> None: for value, column in zip(row, header): columns[column].append(value) for column, transformation in extra_columns.items(): - columns[column] = [transformation({k: v[index] for k, v in columns.items()}) for index in range(len(rows))] + columns[column] = [ + transformation({k: v[index] for k, v in columns.items()}) + for index in range(len(rows)) + ] return columns @@ -488,14 +519,19 @@ def eternal_oom(argv: list[str]) -> None: columns: dict[str, list[Any]] merged_data_frame: MergedDataFrame = dict( - header=[column + '_x' for column in left] + [column + '_y' for column in right], + header=[column + '_x' for column in left] + + [column + '_y' for column in right], columns={}, ) for column in merged_data_frame['header']: merged_data_frame['columns'][column] = [] - common_values: set[Any] = {left_value for left_value in index['left'] if left_value in index['right']} + common_values: set[Any] = { + left_value + for left_value in index['left'] + if left_value in index['right'] + } class RowMatch(TypedDict): left_row_index: int @@ -518,8 +554,14 @@ def eternal_oom(argv: list[str]) -> None: values[ common_row[ cast( - Literal['left_row_index' | 'right_row_index'], - 'left_row_index' if index_name == 'left' else 'right_row_index' if index_name == 'right' else raise_not_implemented(), + Literal[ + 'left_row_index' | 'right_row_index' + ], + 'left_row_index' + if index_name == 'left' + else 'right_row_index' + if index_name == 'right' + else raise_not_implemented(), ) ] ] @@ -539,9 +581,18 @@ def eternal_oom(argv: list[str]) -> None: assert ascending is False t1 = [ o['row_index'] - for o in sorted([dict(row_index=row_index, value=value) for row_index, value in enumerate(data_frame[by[0]])], key=lambda x: x['value'])[::-1] + for o in sorted( + [ + dict(row_index=row_index, value=value) + for row_index, value in enumerate(data_frame[by[0]]) + ], + key=lambda x: x['value'], + )[::-1] ] - return {column: [values[row_index] for row_index in t1] for column, values in data_frame.items()} + return { + column: [values[row_index] for row_index in t1] + for column, values in data_frame.items() + } def pandas_filter_values(data_frame, condition): shape = [ @@ -549,11 +600,25 @@ def eternal_oom(argv: list[str]) -> None: ] if shape[0] > 0: shape.append(len(list(data_frame.values())[0])) - t1 = [row_index for row_index in range(shape[1]) if condition({column: values[row_index] for column, values in data_frame.items()})] - return {column: [values[row_index] for row_index in t1] for column, values in data_frame.items()} + t1 = [ + row_index + for row_index in range(shape[1]) + if condition( + { + column: values[row_index] + for column, values in data_frame.items() + } + ) + ] + return { + column: [values[row_index] for row_index in t1] + for column, values in data_frame.items() + } def pandas_row(data_frame, row_index): - return {column: values[row_index] for column, values in data_frame.items()} + return { + column: values[row_index] for column, values in data_frame.items() + } def pandas_shape(data_frame): columns_count = len(data_frame) @@ -580,7 +645,9 @@ def eternal_oom(argv: list[str]) -> None: def oom_get_processes( extra_filter=None, ): - with io.BytesIO(subprocess.check_output('ps -e -o pid,rss,user,%cpu', shell=True)) as f: + with io.BytesIO( + subprocess.check_output('ps -e -o pid,rss,user,%cpu', shell=True) + ) as f: t1 = pandas_data_frame( f.read().decode('utf-8').splitlines(), ps_regex(4), @@ -594,7 +661,11 @@ def eternal_oom(argv: list[str]) -> None: del t1['%CPU'] assert set(t1.keys()) == set(['PID', 'RSS', 'USER', 'CPU']) - t5 = subprocess.check_output('ps -e -o pid,args', shell=True).decode('utf-8').splitlines() + t5 = ( + subprocess.check_output('ps -e -o pid,args', shell=True) + .decode('utf-8') + .splitlines() + ) t6 = pandas_data_frame( t5, r'^\s*(\d+)\s(.*)$', @@ -614,7 +685,12 @@ def eternal_oom(argv: list[str]) -> None: if extra_filter is None: extra_filter = lambda *args: True - t7 = pandas_filter_values(t11, lambda row: row['PID_x'] != self_pid and not 'freelancer' in row['COMMAND_y'] and extra_filter(row)) + t7 = pandas_filter_values( + t11, + lambda row: row['PID_x'] != self_pid + and not 'freelancer' in row['COMMAND_y'] + and extra_filter(row), + ) t8 = pandas_sort_values(t7, by=['RSS_x'], ascending=False) t9 = pandas_sort_values(t7, by=['CPU_x'], ascending=False) @@ -696,7 +772,9 @@ def eternal_oom(argv: list[str]) -> None: if t11['total_cpu'] > options.cpu_limit: oom_display_rows(t11['by_cpu']) - free_before_oom = options.memory_limit - current_memory_stats['mem_used'] + free_before_oom = ( + options.memory_limit - current_memory_stats['mem_used'] + ) print( 'available %5.2f %% out of %5.2f %% of cpu limit before OOC' @@ -748,7 +826,10 @@ def eternal_oom(argv: list[str]) -> None: if last_cpu_high is None: last_cpu_high = datetime.datetime.now().timestamp() - if datetime.datetime.now().timestamp() - last_cpu_high > options.cpu_wait: + if ( + datetime.datetime.now().timestamp() - last_cpu_high + > options.cpu_wait + ): last_cpu_high = None del last_total_cpu[:] return True @@ -770,8 +851,15 @@ def eternal_oom(argv: list[str]) -> None: mem_stat = memory_stats() mem_used = mem_stat['mem_used'] - if options.memory_limit < mem_stat['mem_total'] and not oom_mem_high(mem_stat['mem_total'] - (mem_stat['mem_total'] - options.memory_limit) / 2): - extra_filters = lambda row: 'chrome' in row['COMMAND_y'] and '--type=renderer' in row['COMMAND_y'] or not 'chrome' in row['COMMAND_y'] + if options.memory_limit < mem_stat['mem_total'] and not oom_mem_high( + mem_stat['mem_total'] + - (mem_stat['mem_total'] - options.memory_limit) / 2 + ): + extra_filters = lambda row: ( + 'chrome' in row['COMMAND_y'] + and '--type=renderer' in row['COMMAND_y'] + or not 'chrome' in row['COMMAND_y'] + ) else: extra_filters = None @@ -813,7 +901,9 @@ def eternal_oom(argv: list[str]) -> None: def resilient_vlc(stream=None): if stream is None: - streams_path = os.path.join(os.environ['CACHE_PATH'], 'resilient-vlc-streams.json') + streams_path = os.path.join( + os.environ['CACHE_PATH'], 'resilient-vlc-streams.json' + ) if os.path.exists(streams_path): with io.open(streams_path, 'r') as f: @@ -854,7 +944,9 @@ def resilient_vlc(stream=None): 'main interface error', ] ] - ) and any([o in t1 for o in ['pulse audio output debug: underflow']]): + ) and any( + [o in t1 for o in ['pulse audio output debug: underflow']] + ): print('shit') p.kill() while True: @@ -932,7 +1024,12 @@ def eternal_firefox( ) as p: try: if debug: - assert subprocess.check_call(['notify-send', '%s:Starting' % group_name]) == 0 + assert ( + subprocess.check_call( + ['notify-send', '%s:Starting' % group_name] + ) + == 0 + ) # t3 = '' for k in range(300): @@ -1017,10 +1114,18 @@ def eternal_firefox( reposition() if debug: - assert subprocess.check_call(['notify-send', '%s:Started' % group_name]) == 0 + assert ( + subprocess.check_call( + ['notify-send', '%s:Started' % group_name] + ) + == 0 + ) start = datetime.datetime.now() - is_to_restart = lambda: (datetime.datetime.now() - start).total_seconds() >= 900 * 4 + is_to_restart = ( + lambda: (datetime.datetime.now() - start).total_seconds() + >= 900 * 4 + ) polling_count = 0 while not is_to_restart(): @@ -1033,7 +1138,12 @@ def eternal_firefox( polling_count += 1 if debug: - assert subprocess.check_call(['notify-send', '%s:Closing' % group_name]) == 0 + assert ( + subprocess.check_call( + ['notify-send', '%s:Closing' % group_name] + ) + == 0 + ) # assert os.system('wmctrl -i -c %s' % t2) == 0 assert ( @@ -1069,7 +1179,12 @@ def eternal_firefox( pprint.pprint([p.pid, '20 seconds timeout', 'kill']) p.kill() if debug: - assert subprocess.check_call(['notify-send', '%s:Closed' % group_name]) == 0 + assert ( + subprocess.check_call( + ['notify-send', '%s:Closed' % group_name] + ) + == 0 + ) def resilient_ethernet(ip_addr, ethernet_device): @@ -1084,7 +1199,9 @@ do ping -c 3 -w 3 -W 1 {{IP_ADDR}} || (\ ); \ sleep 10; clear; date; \ done' - """.replace('{{IP_ADDR}}', ip_addr).replace('{{ETHERNET_DEVICE}}}', ethernet_device), + """.replace('{{IP_ADDR}}', ip_addr).replace( + '{{ETHERNET_DEVICE}}}', ethernet_device + ), shell=True, ) @@ -1171,7 +1288,13 @@ def http_server(argv): # 'ping', '-w', '1', # options.host # ]) - assert options.host in sum([[o2.local for o2 in o.addr_info] for o in commands_os.interfaces_index()], []) + assert options.host in sum( + [ + [o2.local for o2 in o.addr_info] + for o in commands_os.interfaces_index() + ], + [], + ) except Exception: raise RuntimeError('invalid ip address %s' % options.host) @@ -1212,9 +1335,16 @@ def http_server(argv): ) ) - assert all([not re.compile(r'^[A-Za-z-]+ [a-z0-9A-Z-\.]+$').match(o) is None for o in options.response_headers]) + assert all( + [ + not re.compile(r'^[A-Za-z-]+ [a-z0-9A-Z-\.]+$').match(o) is None + for o in options.response_headers + ] + ) - location_section = ('location / {deny all;}location /%s/ {alias %s/;%s%s}') % ( + location_section = ( + 'location / {deny all;}location /%s/ {alias %s/;%s%s}' + ) % ( path, APP_DIR, '\n'.join(['add_header %s;' % o for o in options.response_headers]), @@ -1422,7 +1552,10 @@ def pass_ssh_osx(argv): t1 = options.pass_option assert len(t1) > 0 - print('select on of pass names\n%s' % '\n'.join(['%d: %s' % (k, v) for k, v in enumerate(t1)])) + print( + 'select on of pass names\n%s' + % '\n'.join(['%d: %s' % (k, v) for k, v in enumerate(t1)]) + ) while True: try: @@ -1486,7 +1619,9 @@ def pass_ssh_osx(argv): p.wait(1) assert p.poll() == 0 - with subprocess.Popen(ssh_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + with subprocess.Popen( + ssh_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) as p: password = None last_chunk = None @@ -1542,10 +1677,23 @@ def pass_ssh_osx(argv): if options.debug: pprint.pprint(last_chunk['data']) - if last_chunk['data'].endswith('\r\n[0]'.encode('utf-8')) and last_chunk['data'].rfind(pinentry_delimeter) != -1: + if ( + last_chunk['data'].endswith('\r\n[0]'.encode('utf-8')) + and last_chunk['data'].rfind(pinentry_delimeter) != -1 + ): last_line = last_chunk['data'].splitlines()[-2] else: - raise RuntimeError('gpg failure %s' % str(last_chunk['data'][max(last_chunk['data'].find(pinentry_delimeter), -128) :])) + raise RuntimeError( + 'gpg failure %s' + % str( + last_chunk['data'][ + max( + last_chunk['data'].find(pinentry_delimeter), + -128, + ) : + ] + ) + ) pos2 = last_line.rfind(pinentry_delimeter) if pos2 == -1: @@ -1593,7 +1741,9 @@ def vpn(argv: list[str]) -> None: python_path: list[str] if (pathlib.Path(__file__).parent / 'env3').exists(): - python_path = [str(pathlib.Path(__file__).parent / 'env3' / 'bin' / 'python3')] + python_path = [ + str(pathlib.Path(__file__).parent / 'env3' / 'bin' / 'python3') + ] elif (pathlib.Path(__file__).parent.parent.parent.parent / 'm').exists(): python_path = [ str(pathlib.Path(__file__).parent.parent.parent.parent / 'm'), @@ -1639,14 +1789,28 @@ def player_v1(folder_url, item_id): t7 = t5[k] t9 = urllib.parse.unquote(os.path.split(t7)[1]) progress_bar.set_description('%03d %s' % (k, t9)) - with subprocess.Popen(['ffprobe', '-hide_banner', '-i', t7], stderr=subprocess.PIPE, stdout=subprocess.PIPE) as p: + with subprocess.Popen( + ['ffprobe', '-hide_banner', '-i', t7], + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as p: p.wait() assert p.returncode == 0 t8 = p.stderr.read().decode('utf-8') assert isinstance(t8, str) # print(t8) with subprocess.Popen( - ['ffplay', '-hide_banner', '-nodisp', '-autoexit', '-loop', '1', t7], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + [ + 'ffplay', + '-hide_banner', + '-nodisp', + '-autoexit', + '-loop', + '1', + t7, + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, ) as p: p.wait() assert p.returncode == 0 @@ -1710,7 +1874,9 @@ def pm_service(argv): wu = 0 while True: - subprocess.check_call(['osascript', '-e', 'tell application "Finder" to sleep']) + subprocess.check_call( + ['osascript', '-e', 'tell application "Finder" to sleep'] + ) subprocess.check_call( ['pmset', 'sleepnow'], stdout=subprocess.DEVNULL, @@ -1749,9 +1915,16 @@ def pm_service(argv): # -E 'powerd.*TurnedOn.*UserIsActive' | head -n 1 #''', shell=True).decode('utf-8') - if not cmd is None and ('TurnedOn' in cmd or 'PrevIdle' in cmd or 'PMRD: kIOMessageSystemWillPowerOn' in cmd): + if not cmd is None and ( + 'TurnedOn' in cmd + or 'PrevIdle' in cmd + or 'PMRD: kIOMessageSystemWillPowerOn' in cmd + ): if ( - ('AppleMultitouchDevice' in cmd and 'tp' in options.events) + ( + 'AppleMultitouchDevice' in cmd + and 'tp' in options.events + ) or ('AppleACPIButton' in cmd and 'pb' in options.events) or ('eventType:29' in cmd and 'kb' in options.events) ): @@ -1771,7 +1944,11 @@ def pm_service(argv): ) ) else: - print('\r%s wu : %d, la : %s' % (datetime.datetime.now().isoformat(), wu, action), end='') + print( + '\r%s wu : %d, la : %s' + % (datetime.datetime.now().isoformat(), wu, action), + end='', + ) if action == 'wake-up': break @@ -1890,7 +2067,9 @@ def scrap_yt_music(argv: list[str]) -> None: break if p is None and not current_name is None: - output_name = os.path.join(options.library_path, '%s.mp3' % current_name) + output_name = os.path.join( + options.library_path, '%s.mp3' % current_name + ) logging.info('audio_record, new recording') p = subprocess.Popen( ['sox', '-d', output_name], @@ -1928,7 +2107,9 @@ def scrap_yt_music(argv: list[str]) -> None: target=functools.partial( http_events, context=context, - res_cb=lambda *args, **kwargs: context['http_on_event'](*args, **kwargs), + res_cb=lambda *args, **kwargs: context['http_on_event']( + *args, **kwargs + ), ) ), threading.Thread( @@ -1943,7 +2124,9 @@ def scrap_yt_music(argv: list[str]) -> None: def http_on_event(event, events): with context['track_cv']: if 'title' in event and event['title'].strip() != '': - context['track_name'] = str(event['title'])[:128].replace('\n', '') + context['track_name'] = str(event['title'])[:128].replace( + '\n', '' + ) else: context['track_name'] = None @@ -2049,6 +2232,156 @@ def loginctl(argv: list[str]) -> None: raise NotImplementedError +class Battery: + def __init__( + self, + should_start=None, + ): + if should_start is None: + should_start = False + + assert isinstance(should_start, bool) + + self.last_check = None + self.period = 10 + self.is_running = should_start + + def check_is_needed(self): + now = datetime.datetime.now(tz=datetime.timezone.utc) + + is_needed = None + + if self.last_check is None: + is_needed = True + else: + if (now - self.last_check).total_seconds() >= self.period: + is_needed = True + else: + is_needed = False + + if is_needed: + self.last_check = now + + return is_needed + + def run(self): + while True: + self.check() + + time.sleep(self.period) + + def terminate(self): + self.is_running = False + + def wait(self, *args, **kwargs): + if self.is_running: + raise NotImplementedError + + def poll(self): + if self.is_running: + return None + else: + return 0 + + @property + def percentage_low(self) -> int: + try: + return int( + subprocess.check_output( + r""" + cat /etc/UPower/UPower.conf | grep -Po '^PercentageLow=\d+' + """, + shell=True, + ) + .decode('utf-8') + .strip() + .split('=')[1] + ) + except: + logger.exception('') + return 15 + + @property + def percentage_critical(self) -> int: + try: + return int( + subprocess.check_output( + r""" + cat /etc/UPower/UPower.conf | grep -Po '^PercentageCritical=\d+' + """, + shell=True, + ) + .decode('utf-8') + .strip() + .split('=')[1] + ) + except: + logger.exception('') + return 10 + + def check(self): + try: + if not self.check_is_needed(): + return + + t1 = subprocess.check_output( + ['upower', '-d'], + timeout=1, + ).decode('utf-8') + t2 = [o for o in t1.splitlines() if 'percentage' in o.lower()] + t4 = [o for o in t1.splitlines() if 'state' in o.lower()] + t3 = float(t2[0].split(':')[1].strip()[:-1]) + # t5 = any(['discharging' in o.lower() for o in t4]) + state = [[o2.strip() for o2 in o.lower().split()][1] for o in t4][1] + + t5 = state != 'charging' + # t5 = True + if t3 < self.percentage_critical: + logging.error( + json.dumps( + dict( + msg='too low', + t3=t3, + t5=t5, + state=state, + ) + ) + ) + if t5: + subprocess.check_call(['systemctl', 'suspend']) + elif t3 < self.percentage_low: + msg = 'battery near low' + logging.error( + json.dumps( + dict( + msg=msg, + t3=t3, + t5=t5, + state=state, + ) + ) + ) + if t5: + subprocess.check_call( + [ + 'notify-send', + '-t', + '%d' % (5 * 1000), + msg, + '% 5.2f' % t3, + ] + ) + else: + pass + print( + '\r%s % 5.2f%% %s' + % (datetime.datetime.now().isoformat(), t3, str(t5)), + end='', + ) + except Exception: + logging.error(traceback.format_exc()) + + def desktop_services(argv): parser = optparse.OptionParser() parser.add_option( @@ -2129,7 +2462,9 @@ def desktop_services(argv): t2 = [] try: - t1 = subprocess.check_output(['swaymsg', '-t', 'get_tree']).decode('utf-8') + t1 = subprocess.check_output( + ['swaymsg', '-t', 'get_tree'] + ).decode('utf-8') t2 = json.loads(t1) except Exception: logging.error(traceback.format_exc()) @@ -2187,127 +2522,6 @@ def desktop_services(argv): return len(t3) > 0 and t4 - class Battery: - def __init__( - self, - should_start=None, - ): - if should_start is None: - should_start = False - - assert isinstance(should_start, bool) - - self.last_check = None - self.period = 10 - self.is_running = should_start - - def check_is_needed(self): - now = datetime.datetime.now(tz=datetime.timezone.utc) - - is_needed = None - - if self.last_check is None: - is_needed = True - else: - if (now - self.last_check).total_seconds() >= self.period: - is_needed = True - else: - is_needed = False - - if is_needed: - self.last_check = now - - return is_needed - - def run(self): - while True: - self.check() - - time.sleep(self.period) - - def terminate(self): - self.is_running = False - - def wait(self, *args, **kwargs): - if self.is_running: - raise NotImplementedError - - def poll(self): - if self.is_running: - return None - else: - return 0 - - @property - def percentage_low(self) -> int: - try: - return int( - subprocess.check_output( - r""" - cat /etc/UPower/UPower.conf | grep -Po '^PercentageLow=\d+' - """, - shell=True, - ) - .decode('utf-8') - .strip() - .split('=')[1] - ) - except: - logger.exception('') - return 15 - - @property - def percentage_critical(self) -> int: - try: - return int( - subprocess.check_output( - r""" - cat /etc/UPower/UPower.conf | grep -Po '^PercentageCritical=\d+' - """, - shell=True, - ) - .decode('utf-8') - .strip() - .split('=')[1] - ) - except: - logger.exception('') - return 10 - - def check(self): - try: - if not self.check_is_needed(): - return - - t1 = subprocess.check_output( - ['upower', '-d'], - timeout=1, - ).decode('utf-8') - t2 = [o for o in t1.splitlines() if 'percentage' in o.lower()] - t4 = [o for o in t1.splitlines() if 'state' in o.lower()] - t3 = float(t2[0].split(':')[1].strip()[:-1]) - t5 = any(['discharging' in o.lower() for o in t4]) - if t3 < self.percentage_critical and t5: - logging.error(json.dumps(dict(msg='too low', t3=t3, t5=t5))) - subprocess.check_call(['systemctl', 'suspend']) - elif t3 < self.percentage_low and t5: - msg = 'battery near low' - logging.error(json.dumps(dict(msg=msg, t3=t3, t5=t5))) - subprocess.check_call( - [ - 'notify-send', - '-t', - '%d' % (5 * 1000), - msg, - '% 5.2f' % t3, - ] - ) - else: - pass - print('\r%s % 5.2f%% %s' % (datetime.datetime.now().isoformat(), t3, str(t5)), end='') - except Exception: - logging.error(traceback.format_exc()) - class Backlight: class Direction(enum.Enum): increase = 'increase' @@ -2325,7 +2539,9 @@ def desktop_services(argv): @classmethod def dpms_get(cls): try: - t1 = subprocess.check_output(['swaymsg', '-r', '-t', 'get_outputs'], timeout=1) + t1 = subprocess.check_output( + ['swaymsg', '-r', '-t', 'get_outputs'], timeout=1 + ) t2 = t1.decode('utf-8') t3 = json.loads(t2) t4 = [ @@ -2582,8 +2798,12 @@ def desktop_services(argv): ]: if os.path.exists('/sys/bus/platform/devices/applesmc.768'): return 'applesmc.768' - if os.path.exists('/sys/devices/system/cpu/intel_pstate/no_turbo'): + elif os.path.exists( + '/sys/devices/system/cpu/intel_pstate/no_turbo' + ): return 'intel_pstate' + elif os.path.exists('/sys/devices/system/cpu/amd_pstate'): + return 'amd_pstate' else: raise NotImplementedError @@ -2614,6 +2834,15 @@ echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; echo 40 > /sys/devices/system/cpu/intel_pstate/max_perf_pct; echo 900000 | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; + """, + shell=True, + ) + elif cls.profile() == 'amd_pstate': + subprocess.check_call( + r""" +echo powersave | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; +echo 800000 | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; +echo 0 | tee /sys/devices/system/cpu/cpu*/cpufreq/boost """, shell=True, ) @@ -2645,6 +2874,15 @@ echo powersave | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; echo 60 > /sys/devices/system/cpu/intel_pstate/max_perf_pct; echo 1200000 | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; + """, + shell=True, + ) + elif cls.profile() == 'amd_pstate': + subprocess.check_call( + r""" +echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; +cat /sys/devices/system/cpu/cpu0/cpufreq/amd_pstate_max_freq | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; +echo 1 | tee /sys/devices/system/cpu/cpu*/cpufreq/boost """, shell=True, ) @@ -2779,7 +3017,11 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; after_resume='echo after_resume; pkill --signal SIGUSR1 swayidle;', ) self.last_force_idle = None - self.commands.update(timeout2='echo timeout2; {swaylock_cmd};'.format(swaylock_cmd=self.commands['swaylock_cmd2'])) + self.commands.update( + timeout2='echo timeout2; {swaylock_cmd};'.format( + swaylock_cmd=self.commands['swaylock_cmd2'] + ) + ) self.swayidle = subprocess.Popen( r""" exec swayidle -d -w \ @@ -2815,14 +3057,22 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; self.bg_terminate = False def skip_loop_long_ago(self): - if self.last_skip_loop is None or (datetime.datetime.now() - self.last_skip_loop).total_seconds() >= 30: + if ( + self.last_skip_loop is None + or ( + datetime.datetime.now() - self.last_skip_loop + ).total_seconds() + >= 30 + ): self.last_skip_loop = datetime.datetime.now() return True else: return False def background_check(self): - if (self.bg is None or not self.bg.poll() is None) and not self.bg_terminate: + if ( + self.bg is None or not self.bg.poll() is None + ) and not self.bg_terminate: if not options.background_image is None: self.bg = subprocess.Popen( [ @@ -2849,7 +3099,13 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; self.swayidle.stdin.flush() def force_idle(self): - if self.last_force_idle is None or (datetime.datetime.now() - self.last_force_idle).total_seconds() >= 10: + if ( + self.last_force_idle is None + or ( + datetime.datetime.now() - self.last_force_idle + ).total_seconds() + >= 10 + ): self.last_force_idle = datetime.datetime.now() return True else: @@ -2894,7 +3150,9 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; self.data.append(chunk) if b'\n' in chunk['data']: - total = b''.join([o['data'] for o in self.data]).decode('utf-8') + total = b''.join([o['data'] for o in self.data]).decode( + 'utf-8' + ) sep_pos = total.rfind('\n') lines = total[:sep_pos].splitlines() self.data = [ @@ -2933,7 +3191,10 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; while True: logging.info('retry i = %d, cnt = %d' % (i, cnt)) - if not (subprocess.call(['swaymsg', '-t', 'get_version']) == 0): + if not ( + subprocess.call(['swaymsg', '-t', 'get_version']) + == 0 + ): continue if cb() == 0: @@ -2943,12 +3204,20 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; i += 1 - if len(new_events) > 0 or len(self.events) > 0 and self.skip_loop_long_ago(): + if ( + len(new_events) > 0 + or len(self.events) > 0 + and self.skip_loop_long_ago() + ): self.events.extend(new_events) skip_loop = False - if all([o in ['t1', 't4'] for o in self.events]) and VLC.vlc_is_playing_fullscreen() and self.backlight.dpms: + if ( + all([o in ['t1', 't4'] for o in self.events]) + and VLC.vlc_is_playing_fullscreen() + and self.backlight.dpms + ): skip_loop = True logging.info( 'skip loop, %s' @@ -2962,9 +3231,17 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; ], ) ) - elif len(new_events) == 0 and len(self.events) > 1 and all([o in ['t1', 't4'] for o in self.events]): + elif ( + len(new_events) == 0 + and len(self.events) > 1 + and all([o in ['t1', 't4'] for o in self.events]) + ): self.events = ['t4'] - elif len(self.events) > 1 and (self.events == ['t1', 't4', 't5', 't5'] or self.events == ['t1', 't5', 't5'] or self.events == ['t1', 't5']): + elif len(self.events) > 1 and ( + self.events == ['t1', 't4', 't5', 't5'] + or self.events == ['t1', 't5', 't5'] + or self.events == ['t1', 't5'] + ): for o in new_events: self.release_lock() @@ -2980,7 +3257,9 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; # subprocess.check_call(self.commands['lock'], shell=True) logging.info('started t1') if self.force_idle(): - subprocess.check_call(self.commands['timeout1'], shell=True) + subprocess.check_call( + self.commands['timeout1'], shell=True + ) logging.info('done t1') self.release_lock() elif o == 't2': @@ -2991,12 +3270,29 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; msg='loginctl lock started', ) while True: - if not subprocess.call(self.commands['lock'], shell=True) == 0: + if ( + not subprocess.call( + self.commands['lock'], shell=True + ) + == 0 + ): continue - if not subprocess.call(self.commands['timeout2'], shell=True) == 0: + if ( + not subprocess.call( + self.commands['timeout2'], + shell=True, + ) + == 0 + ): # continue pass - if not subprocess.call(self.commands['timeout1'], shell=True) == 0: + if ( + not subprocess.call( + self.commands['timeout1'], + shell=True, + ) + == 0 + ): continue break logging.info('done lock') @@ -3006,24 +3302,42 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; elif o == 't4': logging.info('started t4') if self.force_idle(): - subprocess.check_call(self.commands['lock'], shell=True) - subprocess.call(self.commands['timeout2'], shell=True) - subprocess.check_call(self.commands['timeout1'], shell=True) + subprocess.check_call( + self.commands['lock'], shell=True + ) + subprocess.call( + self.commands['timeout2'], shell=True + ) + subprocess.check_call( + self.commands['timeout1'], shell=True + ) logging.info('done t4') self.release_lock() elif o == 't5': logging.info('started timeout resume') if self.force_idle(): - subprocess.check_call(self.commands['lock'], shell=True) + subprocess.check_call( + self.commands['lock'], shell=True + ) retry( - lambda: subprocess.call(self.commands['resume'], shell=True), + lambda: subprocess.call( + self.commands['resume'], shell=True + ), ) logging.info('done timeout resume') elif o == 't6': logging.info('started before-sleep') if self.force_idle(): - (subprocess.call(self.commands['timeout2'], shell=True),) - (subprocess.check_call(self.commands['timeout1'], shell=True),) + ( + subprocess.call( + self.commands['timeout2'], shell=True + ), + ) + ( + subprocess.check_call( + self.commands['timeout1'], shell=True + ), + ) logging.info('done before-sleep') self.release_lock() elif o == 't7': @@ -3031,7 +3345,12 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; # if self.force_idle(): # subprocess.check_call(self.commands['lock'], shell=True) while True: - if subprocess.call(self.commands['resume'], shell=True) == 0: + if ( + subprocess.call( + self.commands['resume'], shell=True + ) + == 0 + ): break else: time.sleep(0.5) @@ -3056,7 +3375,15 @@ echo schedutil | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; self.background_check() if options.polkit_service: - services.extend([subprocess.Popen(['/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1'])]) + services.extend( + [ + subprocess.Popen( + [ + '/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1' + ] + ) + ] + ) services.extend( [ @@ -3191,12 +3518,24 @@ def gnome_shortcuts(argv: list[str]) -> None: 'set', 'org.gnome.settings-daemon.plugins.media-keys', 'custom-keybindings', - '[%s]' % ','.join(["'%s'" % ('/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/') % o for o in range(command_id + 1)]), + '[%s]' + % ','.join( + [ + "'%s'" + % ( + '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/' + ) + % o + for o in range(command_id + 1) + ] + ), ), ( 'gsettings', 'set', - ('org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/') + ( + 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/' + ) % command_id, 'name', name, @@ -3204,7 +3543,9 @@ def gnome_shortcuts(argv: list[str]) -> None: ( 'gsettings', 'set', - ('org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/') + ( + 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/' + ) % command_id, 'command', command, @@ -3212,7 +3553,9 @@ def gnome_shortcuts(argv: list[str]) -> None: ( 'gsettings', 'set', - ('org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/') + ( + 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/' + ) % command_id, 'binding', binding, @@ -3230,7 +3573,10 @@ def gnome_shortcuts(argv: list[str]) -> None: [ 'gsettings', 'get', - ('org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:%s') % o, + ( + 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:%s' + ) + % o, k, ] ) @@ -3295,7 +3641,9 @@ def socat_ssh(argv): dest='gateway_command', default=None, type=str, - help=('a shell command that forwards ssh socket data somewhere else, like busybox nc 127.0.0.1 $(cat remote-ssh.port)'), + help=( + 'a shell command that forwards ssh socket data somewhere else, like busybox nc 127.0.0.1 $(cat remote-ssh.port)' + ), ) options, args = parser.parse_args(argv) @@ -3428,10 +3776,18 @@ def share_wifi(argv): print('enter password:') - pw = subprocess.check_output('read -s PW; echo -n $PW', shell=True).decode('utf-8') + pw = subprocess.check_output('read -s PW; echo -n $PW', shell=True).decode( + 'utf-8' + ) if len(pw) == 0: - pw = subprocess.check_output('pwgen -syn 20 1', shell=True).decode('utf-8').strip() - with subprocess.Popen(['qrencode', '-t', 'UTF8'], stdin=subprocess.PIPE) as p: + pw = ( + subprocess.check_output('pwgen -syn 20 1', shell=True) + .decode('utf-8') + .strip() + ) + with subprocess.Popen( + ['qrencode', '-t', 'UTF8'], stdin=subprocess.PIPE + ) as p: p.stdin.write(pw.encode('utf-8')) p.stdin.flush() p.stdin.close() @@ -3498,7 +3854,9 @@ def share_wifi(argv): if shutdown: break - if (datetime.datetime.now() - last_timestamp).total_seconds() > options.restart_delay: + if ( + datetime.datetime.now() - last_timestamp + ).total_seconds() > options.restart_delay: restart = True last_timestamp = datetime.datetime.now() @@ -3509,119 +3867,6 @@ def share_wifi(argv): time.sleep(1) -def status(argv): - import inspect - import textwrap - - assert isinstance(argv, list) and all([isinstance(o, str) for o in argv]) - - class c1(optparse.IndentedHelpFormatter): - def format_option(self, *args, **kwargs): - f1 = lambda text, width: '\n'.join([textwrap.fill('\t' + o, width, replace_whitespace=False) for o in text.splitlines()]).splitlines() - t1 = inspect.getsource(optparse.IndentedHelpFormatter.format_option) - t2 = ( - '\n'.join([o[4:] for o in t1.splitlines()[:]]) - .replace( - 'textwrap.wrap', - 'f1', - ) - .replace('format_option', 'f2') - ) - exec(t2, dict(f1=f1), locals()) - return locals()['f2'](self, *args, **kwargs) - - parser = optparse.OptionParser( - formatter=c1( - width=None, - ), - ) - parser.add_option( - '--sh', - dest='sh', - default=[], - action='append', - type=str, - ) - parser.add_option( - '--timeout', - dest='timeout', - default=None, - type=float, - ) - parser.add_option( - '--config', - dest='config', - default=None, - type=str, - help=''.join( - [ - '.json file with array of strings, each is a shell command ', - 'that outputs a separate status text value, ', - 'like\n', - r""" -ping -w 1 -i 0.02 -c 3 | tail -n 2| head -n 1 | grep -Po $'time\\s+.*$' -sensors -j | jq -r '.\"coretemp-isa-0000\".\"Package id 0\".temp1_input|tostring + \" C\"' -printf '%d RPM' $(cat /sys/devices/platform/applesmc.768/fan1_input) -printf '% 3.0f%%' $(upower -d | grep -Po 'percentage:\\s+\\d+(\\.\\d+)?%' | grep -Po '\\d+(\\.\\d+)?' | head -n 1) - """.strip(), - ] - ), - ) - options, args = parser.parse_args(argv) - - if options.timeout is None: - options.timeout = 0.5 - - timeout2 = max(options.timeout, 0.0) - - assert timeout2 >= 0.0 and timeout2 <= 4 - - config = dict() - try: - if not options.config is None: - with io.open(options.config, 'r') as f: - config.update(json.load(f)) - except Exception: - logging.error(traceback.format_exc()) - pass - - options.sh.extend(config.get('sh', [])) - - t1 = [] - - for sh_index, o in enumerate( - [ - *options.sh, - *[ - r""" - A=$(free -h | grep -P Mem: | grep -Po '[\w\.\d]+'); - echo -n $A | awk '{print $2, $7}'; - """, - r""" - date +'%Y-%m-%d %l:%M:%S %p'; - """, - ], - ] - ): - try: - t1.append( - subprocess.check_output( - o, - shell=True, - timeout=timeout2, - ) - .decode('utf-8') - .strip() - ) - except Exception: - t1.append('fail %d' % sh_index) - - t3 = ' | '.join(t1).replace('\n\r', '') - - sys.stdout.write(t3) - sys.stdout.flush() - - def custom_translate( current_string, check, @@ -3730,8 +3975,14 @@ def media_keys(argv): if mode == 'mocp': raise NotImplementedError elif mode == 'playerctl': - pos = float(subprocess.check_output(['playerctl', 'position']).decode('utf-8')) - subprocess.check_call(['playerctl', 'position', '%f' % (pos - float(args[0]))]) + pos = float( + subprocess.check_output(['playerctl', 'position']).decode( + 'utf-8' + ) + ) + subprocess.check_call( + ['playerctl', 'position', '%f' % (pos - float(args[0]))] + ) # msg = player_metadata() else: raise NotImplementedError @@ -3739,8 +3990,14 @@ def media_keys(argv): if mode == 'mocp': raise NotImplementedError elif mode == 'playerctl': - pos = float(subprocess.check_output(['playerctl', 'position']).decode('utf-8')) - subprocess.check_call(['playerctl', 'position', '%f' % (pos + float(args[0]))]) + pos = float( + subprocess.check_output(['playerctl', 'position']).decode( + 'utf-8' + ) + ) + subprocess.check_call( + ['playerctl', 'position', '%f' % (pos + float(args[0]))] + ) # msg = player_metadata() else: raise NotImplementedError @@ -3754,8 +4011,16 @@ def media_keys(argv): else: raise NotImplementedError elif options.command == 'media-lower-volume': - subprocess.check_call(['pactl', 'set-sink-volume', '@DEFAULT_SINK@', '-5%']) - msg = subprocess.check_output(['pactl', 'get-sink-volume', '@DEFAULT_SINK@']).decode('utf-8').strip() + subprocess.check_call( + ['pactl', 'set-sink-volume', '@DEFAULT_SINK@', '-5%'] + ) + msg = ( + subprocess.check_output( + ['pactl', 'get-sink-volume', '@DEFAULT_SINK@'] + ) + .decode('utf-8') + .strip() + ) elif options.command == 'media-toggle-volume': subprocess.check_call( [ @@ -3765,10 +4030,24 @@ def media_keys(argv): 'toggle', ] ) - msg = subprocess.check_output(['pactl', 'get-sink-volume', '@DEFAULT_SINK@']).decode('utf-8').strip() + msg = ( + subprocess.check_output( + ['pactl', 'get-sink-volume', '@DEFAULT_SINK@'] + ) + .decode('utf-8') + .strip() + ) elif options.command == 'media-raise-volume': - subprocess.check_call(['pactl', 'set-sink-volume', '@DEFAULT_SINK@', '+5%']) - msg = subprocess.check_output(['pactl', 'get-sink-volume', '@DEFAULT_SINK@']).decode('utf-8').strip() + subprocess.check_call( + ['pactl', 'set-sink-volume', '@DEFAULT_SINK@', '+5%'] + ) + msg = ( + subprocess.check_output( + ['pactl', 'get-sink-volume', '@DEFAULT_SINK@'] + ) + .decode('utf-8') + .strip() + ) else: raise NotImplementedError @@ -3841,7 +4120,9 @@ def install(argv: list[str]) -> None: final_target = options.target / relative_source - logger.info(dict(final_target=final_target, relative_source=relative_source)) + logger.info( + dict(final_target=final_target, relative_source=relative_source) + ) if final_target.exists(): if not options.overwrite: @@ -3956,6 +4237,7 @@ class Command(enum.Enum): backup = 'backup' pip_resolve = 'pip_resolve' pip_check_conflicts = 'pip_check_conflicts' + color_scheme = 'color_scheme' def pip_check_conflicts( @@ -3984,7 +4266,10 @@ def pip_check_conflicts( def pip_resolve( args: list[str], ) -> None: - from online.fxreader.pr34.commands_typed.pip import pip_resolve, pip_resolve_t + from online.fxreader.pr34.commands_typed.pip import ( + pip_resolve, + pip_resolve_t, + ) parser = argparse.ArgumentParser() parser.add_argument( @@ -4037,7 +4322,9 @@ def commands_cli(argv: Optional[list[str]] = None) -> int: if argv is None: argv = sys.argv[1:] - from online.fxreader.pr34.commands_typed.logging import setup as logging_setup + from online.fxreader.pr34.commands_typed.logging import ( + setup as logging_setup, + ) logging_setup() # logging.getLogger().setLevel(logging.INFO) @@ -4063,6 +4350,8 @@ def commands_cli(argv: Optional[list[str]] = None) -> int: options.command = Command(options._command) if options.command is Command.status: + from .commands_typed.status import run as status + status(args) elif options.command is Command.http_server: http_server(args) @@ -4125,6 +4414,10 @@ def commands_cli(argv: Optional[list[str]] = None) -> int: backup(args) elif options.command is Command.scrap_yt_music: scrap_yt_music(args) + elif options.command is Command.color_scheme: + from .commands_typed.color_scheme import run as color_scheme + + color_scheme(args) elif options.command is Command.vpn: vpn(args) else: diff --git a/python/online/fxreader/pr34/commands_typed/archlinux/cache_db.py b/python/online/fxreader/pr34/commands_typed/archlinux/cache_db.py index 03d60dc..f22e019 100644 --- a/python/online/fxreader/pr34/commands_typed/archlinux/cache_db.py +++ b/python/online/fxreader/pr34/commands_typed/archlinux/cache_db.py @@ -504,9 +504,7 @@ class cache_db_t: snapshots = [dict(zip(snap_columns, raw)) for raw in cur.fetchall()] cur.execute( - 'SELECT id, snapshot_id, name, version, base, desc, filename, ' - 'csize, isize, md5sum, sha256sum, url, arch, builddate, packager ' - 'FROM packages' + 'SELECT id, snapshot_id, name, version, base, desc, filename, csize, isize, md5sum, sha256sum, url, arch, builddate, packager FROM packages' ) pkg_columns = [desc[0] for desc in cur.description] diff --git a/python/online/fxreader/pr34/commands_typed/async_api/fastapi.py b/python/online/fxreader/pr34/commands_typed/async_api/fastapi.py new file mode 100644 index 0000000..9ff821d --- /dev/null +++ b/python/online/fxreader/pr34/commands_typed/async_api/fastapi.py @@ -0,0 +1,68 @@ +import fastapi +import importlib +import pydantic +import functools +import logging +import copy +import uvicorn +import uvicorn.config +import sys + +from .settings import Settings as APISettings + +from typing import ( + Any, + Optional, + Literal, + Annotated, + cast, + Callable, +) + +logger = logging.getLogger(__name__) + + +def create_app() -> fastapi.FastAPI: + app = fastapi.FastAPI() + + logger.info(dict(msg='started loading apps')) + + for app_config in APISettings.singleton().apps: + logger.info(dict(msg='start loading app = {}'.format(app_config))) + app_module, app_method, app_prefix = app_config.split(':') + + app_router = cast( + Callable[[], Any], + getattr(importlib.import_module(app_module), app_method), + )() + + assert isinstance(app_router, fastapi.APIRouter) + + app.include_router( + app_router, + prefix=app_prefix, + # prefix='/', + ) + logger.info(dict(msg='done loading app = {}'.format(app_config))) + logger.info(dict(msg='done loading apps')) + + return app + + +def run(args: list[str]): + logging.basicConfig(level=logging.INFO) + + log_config = copy.deepcopy(uvicorn.config.LOGGING_CONFIG) + + uvicorn.run( + create_app, + host=APISettings.singleton().uvicorn_host, + port=APISettings.singleton().uvicorn_port, + loop='uvloop', + log_config=log_config, + log_level=logging.INFO, + ) + + +if __name__ == '__main__': + run(sys.argv[1:]) diff --git a/python/online/fxreader/pr34/commands_typed/async_api/settings.py b/python/online/fxreader/pr34/commands_typed/async_api/settings.py new file mode 100644 index 0000000..d806260 --- /dev/null +++ b/python/online/fxreader/pr34/commands_typed/async_api/settings.py @@ -0,0 +1,29 @@ +import pydantic +import pydantic_settings + +from typing import ( + ClassVar, + Optional, + Annotated, +) + + +class Settings(pydantic_settings.BaseSettings): + apps: Annotated[ + list[str], + pydantic.Field( + default_factory=list, + ), + ] + + uvicorn_port: int = 80 + uvicorn_host: str = '127.0.0.1' + + _singleton: ClassVar[Optional['Settings']] = None + + @classmethod + def singleton(cls) -> 'Settings': + if cls._singleton is None: + cls._singleton = Settings.model_validate({}) + + return cls._singleton diff --git a/python/online/fxreader/pr34/commands_typed/cli.py b/python/online/fxreader/pr34/commands_typed/cli.py index 3fb9842..a50c135 100644 --- a/python/online/fxreader/pr34/commands_typed/cli.py +++ b/python/online/fxreader/pr34/commands_typed/cli.py @@ -220,9 +220,13 @@ class CLI(abc.ABC): ) -> None: from . import cli_bootstrap - pyproject = cli_bootstrap.pyproject_load(self.projects[project].source_dir / 'pyproject.toml') + pyproject = cli_bootstrap.pyproject_load( + self.projects[project].source_dir / 'pyproject.toml' + ) - dependencies = sum([pyproject.dependencies[o] for o in features], cast(list[str], [])) + dependencies = sum( + [pyproject.dependencies[o] for o in features], cast(list[str], []) + ) pip_find_links: list[pathlib.Path] = [] @@ -264,7 +268,9 @@ class CLI(abc.ABC): force: bool, ) -> None: for k, d in self.dependencies.items(): - whl_glob = self.dist_settings.wheel_dir / ('*%s*.whl' % d.name.replace('.', '_')) + whl_glob = self.dist_settings.wheel_dir / ( + '*%s*.whl' % d.name.replace('.', '_') + ) if len(glob.glob(str(whl_glob))) == 0 or force: if d.source_path.exists(): @@ -304,7 +310,9 @@ class CLI(abc.ABC): def index_get(o: dict[str, Any]) -> tuple[Any, ...]: return (o['path'], o['stat']) - present_files_index = {index_get(o): o for o in present_files} + present_files_index = { + index_get(o): o for o in present_files + } new_files: list[dict[str, Any]] = [] @@ -329,17 +337,33 @@ class CLI(abc.ABC): ] ) - @property + # @property def pkg_config_path( self, - ) -> set[pathlib.Path]: - return { - pathlib.Path(o) - for o in glob.glob( - str(self.dist_settings.env_path / 'lib' / 'python*' / '**' / 'pkgconfig'), - recursive=True, - ) - } + project: Optional[str] = None, + ) -> list[pathlib.Path]: + res: list[pathlib.Path] = [] + + if project: + res.append(self.projects[project].dest_dir / 'lib' / 'pkgconfig') + + res.extend( + [ + pathlib.Path(o) + for o in glob.glob( + str( + self.dist_settings.env_path + / 'lib' + / 'python*' + / '**' + / 'pkgconfig' + ), + recursive=True, + ) + ] + ) + + return res def deploy_wheel( self, @@ -431,6 +455,14 @@ class CLI(abc.ABC): if env is None: env = dict() + env = ( + dict( + # to generate zip for .whl with a reproducible checksum + SOURCE_DATE_EPOCH='0', + ) + | env + ) + extra_args: list[str] = [] pyproject_build_dir = project.build_dir / 'pyproject' @@ -445,7 +477,18 @@ class CLI(abc.ABC): shutil.rmtree(pyproject_build_dir) if len(self.third_party_roots(project_name)) > 0: - extra_args.append('-Csetup-args=%s' % ('-Dthird_party_roots=%s' % json.dumps([str(o.absolute()) for o in self.third_party_roots(project_name)]))) + extra_args.append( + '-Csetup-args=%s' + % ( + '-Dthird_party_roots=%s' + % json.dumps( + [ + str(o.absolute()) + for o in self.third_party_roots(project_name) + ] + ) + ) + ) cmd = [ sys.executable, @@ -531,11 +574,21 @@ class CLI(abc.ABC): preserve_top_path=True, ) - pyproject = cli_bootstrap.pyproject_load(project.source_dir / 'pyproject.toml') + pyproject = cli_bootstrap.pyproject_load( + project.source_dir / 'pyproject.toml' + ) - pyproject_tool = pydantic.RootModel[PyProject.Tool].model_validate(pyproject.tool).root + pyproject_tool = ( + pydantic.RootModel[PyProject.Tool] + .model_validate(pyproject.tool) + .root + ) - if pyproject_tool.meson and pyproject_tool.meson.args and pyproject_tool.meson.args.install: + if ( + pyproject_tool.meson + and pyproject_tool.meson.args + and pyproject_tool.meson.args.install + ): argv = pyproject_tool.meson.args.install + argv cmd = [ @@ -580,16 +633,22 @@ class CLI(abc.ABC): content = f.read() with io.open(o, 'w') as f: - f.write(content.replace('prefix=/', 'prefix=${pcfiledir}/../../')) + f.write( + content.replace('prefix=/', 'prefix=${pcfiledir}/../../') + ) def ninja( self, project_name: str, argv: Optional[list[str]] = None, env: Optional[dict[str, str]] = None, + mode: Optional[Literal['meson', 'pyproject']] = None, ) -> None: project = self.projects[project_name] + if mode is None: + mode = 'meson' + if argv is None: argv = [] @@ -602,7 +661,7 @@ class CLI(abc.ABC): [ shutil_which('ninja', True), '-C', - str(project.build_dir / 'meson'), + str(project.build_dir / mode), *argv, ], env=self.make_env(env=env), @@ -670,18 +729,30 @@ class CLI(abc.ABC): res: list[pathlib.Path] = [] if not project_name is None: - pyproject = cli_bootstrap.pyproject_load(self.projects[project_name].source_dir / 'pyproject.toml') + pyproject = cli_bootstrap.pyproject_load( + self.projects[project_name].source_dir / 'pyproject.toml' + ) for third_party_root in pyproject.third_party_roots: if third_party_root.package: if not third_party_root.module_root: - third_party_root.module_root = third_party_root.package.replace('.', os.path.sep) + third_party_root.module_root = ( + third_party_root.package.replace('.', os.path.sep) + ) if not third_party_root.path: packages = pip_show([third_party_root.package]) assert len(packages) == 1 - third_party_root.path = str(pathlib.Path(packages[0].location) / third_party_root.module_root / 'lib') + third_party_root.path = str( + pathlib.Path(packages[0].location) + / third_party_root.module_root + / 'lib' + ) else: - assert not third_party_root.package and not third_party_root.module_root and third_party_root.path + assert ( + not third_party_root.package + and not third_party_root.module_root + and third_party_root.path + ) res.append(pathlib.Path(third_party_root.path)) @@ -697,8 +768,12 @@ class CLI(abc.ABC): path: Optional[pathlib.Path] = None @property - def meson_toolchains(self) -> dict[str, meson_toolchains_t.res_t.toolchain_t]: - t1 = pathlib.Path(importlib.import_module('online.fxreader.pr34').__path__[0]) + def meson_toolchains( + self, + ) -> dict[str, meson_toolchains_t.res_t.toolchain_t]: + t1 = pathlib.Path( + importlib.import_module('online.fxreader.pr34').__path__[0] + ) toolchains = glob.glob(str(t1 / 'meson' / 'toolchains' / '*')) res: dict[str, CLI.meson_toolchains_t.res_t.toolchain_t] = dict() @@ -723,7 +798,11 @@ class CLI(abc.ABC): ) -> list[str]: from . import argparse as pr34_argparse - if pyproject_tool.meson and pyproject_tool.meson.args and pyproject_tool.meson.args.setup: + if ( + pyproject_tool.meson + and pyproject_tool.meson.args + and pyproject_tool.meson.args.setup + ): extra_args = pyproject_tool.meson.args.setup + extra_args parser = argparse.ArgumentParser() @@ -738,8 +817,13 @@ class CLI(abc.ABC): options, args = pr34_argparse.parse_args(parser, extra_args) if not options.cross_file is None: - if not options.cross_file.exists() and (not options.cross_file.is_absolute() and options.cross_file.stem in self.meson_toolchains): - options.cross_file = self.meson_toolchains[options.cross_file.stem].path + if not options.cross_file.exists() and ( + not options.cross_file.is_absolute() + and options.cross_file.stem in self.meson_toolchains + ): + options.cross_file = self.meson_toolchains[ + options.cross_file.stem + ].path extra_args = ['--cross-file', str(options.cross_file)] + args @@ -751,11 +835,15 @@ class CLI(abc.ABC): force: bool, argv: Optional[list[str]] = None, env: Optional[dict[str, str]] = None, + mode: Optional[Literal['meson', 'pyproject']] = None, # third_party_roots: Optional[list[pathlib.Path]] = None, ) -> None: from . import cli_bootstrap from .os import shutil_rmtree + if mode is None: + mode = 'meson' + project = self.projects[project_name] if argv is None: @@ -764,16 +852,27 @@ class CLI(abc.ABC): if env is None: env = dict() - pyproject = cli_bootstrap.pyproject_load(project.source_dir / 'pyproject.toml') + pyproject = cli_bootstrap.pyproject_load( + project.source_dir / 'pyproject.toml' + ) - pyproject_tool = pydantic.RootModel[PyProject.Tool].model_validate(pyproject.tool).root + pyproject_tool = ( + pydantic.RootModel[PyProject.Tool] + .model_validate(pyproject.tool) + .root + ) logger.info(dict(env=env)) if force: - if (project.build_dir / 'meson').exists(): - logger.info(dict(action='removing build dir', path=project.build_dir / 'meson')) - shutil.rmtree(project.build_dir / 'meson') + if (project.build_dir / mode).exists(): + logger.info( + dict( + action='removing build dir', + path=project.build_dir / mode, + ) + ) + shutil.rmtree(project.build_dir / mode) extra_args: list[str] = [] @@ -783,7 +882,15 @@ class CLI(abc.ABC): ) if len(self.third_party_roots(project_name)) > 0: - extra_args.append('-Dthird_party_roots=%s' % json.dumps([str(o.absolute()) for o in self.third_party_roots(project_name)])) + extra_args.append( + '-Dthird_party_roots=%s' + % json.dumps( + [ + str(o.absolute()) + for o in self.third_party_roots(project_name) + ] + ) + ) cmd = [ # shutil_which( @@ -795,8 +902,11 @@ class CLI(abc.ABC): 'mesonbuild.mesonmain', 'setup', str(project.source_dir), - str(project.build_dir / 'meson'), - '-Dmodes=["meson"]', + str(project.build_dir / mode), + '--pkg-config-path={}'.format( + json.dumps([str(o) for o in self.pkg_config_path(project_name)]) + ), + '-Dmodes=["{}"]'.format(mode), *extra_args, # '-Dpkgconfig.relocatable=true', '-Dprefix=/', @@ -845,14 +955,21 @@ class CLI(abc.ABC): argv, ) - pyproject = cli_bootstrap.pyproject_load(project.source_dir / 'pyproject.toml') + pyproject = cli_bootstrap.pyproject_load( + project.source_dir / 'pyproject.toml' + ) - dependencies = sum([pyproject.dependencies[o] for o in options.features], cast(list[str], [])) + dependencies = sum( + [pyproject.dependencies[o] for o in options.features], + cast(list[str], []), + ) pip_find_links: list[pathlib.Path] = [] if not pyproject.pip_find_links is None: - pip_find_links.extend([o for o in pyproject.pip_find_links if o.exists()]) + pip_find_links.extend( + [o for o in pyproject.pip_find_links if o.exists()] + ) requirements_name_get_res = cli_bootstrap.requirements_name_get( source_dir=project.source_dir, @@ -961,7 +1078,9 @@ class CLI(abc.ABC): assert options.module in [o.name for o in pyproject.modules] - modules: dict[str, cli_bootstrap.PyProject.Module] = {o.name: o for o in pyproject.modules} + modules: dict[str, cli_bootstrap.PyProject.Module] = { + o.name: o for o in pyproject.modules + } module = modules[options.module] @@ -973,7 +1092,8 @@ class CLI(abc.ABC): 'w', ) as f: p = pyproject2['project'] - assert isinstance(p, tomlkit.items.Table) + # assert isinstance(p, tomlkit.items.Table) + assert isinstance(p, MutableMapping) p['name'] = module.name if not pyproject2['tool']: diff --git a/python/online/fxreader/pr34/commands_typed/cli_bootstrap.py b/python/online/fxreader/pr34/commands_typed/cli_bootstrap.py index 5eeea40..755f3e6 100644 --- a/python/online/fxreader/pr34/commands_typed/cli_bootstrap.py +++ b/python/online/fxreader/pr34/commands_typed/cli_bootstrap.py @@ -86,7 +86,9 @@ class PyProject: third_party_roots: list[ThirdPartyRoot] = dataclasses.field( default_factory=lambda: [], ) - requirements: dict[str, pathlib.Path] = dataclasses.field(default_factory=lambda: dict()) + requirements: dict[str, pathlib.Path] = dataclasses.field( + default_factory=lambda: dict() + ) modules: list[Module] = dataclasses.field( default_factory=lambda: [], @@ -132,7 +134,12 @@ def check_dict( else: VT_class = VT - assert all([isinstance(k, KT) and (VT_class is None or isinstance(v, VT_class)) for k, v in value2.items()]) + assert all( + [ + isinstance(k, KT) and (VT_class is None or isinstance(v, VT_class)) + for k, v in value2.items() + ] + ) if VT is None: return cast( @@ -251,7 +258,12 @@ def pyproject_load( str, ) - if 'tool' in content and isinstance(content['tool'], dict) and tool_name in content['tool'] and isinstance(content['tool'][tool_name], dict): + if ( + 'tool' in content + and isinstance(content['tool'], dict) + and tool_name in content['tool'] + and isinstance(content['tool'][tool_name], dict) + ): pr34_tool = check_dict( check_dict( content['tool'], @@ -264,7 +276,9 @@ def pyproject_load( res.early_features = pr34_tool['early_features'] if 'pip_find_links' in pr34_tool: - res.pip_find_links = [d.parent / pathlib.Path(o) for o in pr34_tool['pip_find_links']] + res.pip_find_links = [ + d.parent / pathlib.Path(o) for o in pr34_tool['pip_find_links'] + ] if 'runtime_libdirs' in pr34_tool: res.runtime_libdirs = [ @@ -283,7 +297,9 @@ def pyproject_load( if 'third_party_roots' in pr34_tool: for o in check_list(pr34_tool['third_party_roots']): o2 = check_dict(o, str, str) - assert all([k in {'package', 'module_root', 'path'} for k in o2]) + assert all( + [k in {'package', 'module_root', 'path'} for k in o2] + ) res.third_party_roots.append( PyProject.ThirdPartyRoot( @@ -297,7 +313,9 @@ def pyproject_load( res.requirements = { k: d.parent / pathlib.Path(v) # pathlib.Path(o) - for k, v in check_dict(pr34_tool['requirements'], str, str).items() + for k, v in check_dict( + pr34_tool['requirements'], str, str + ).items() } if 'modules' in pr34_tool: @@ -364,7 +382,10 @@ class BootstrapSettings: ).strip() ) pip_check_conflicts: Optional[bool] = dataclasses.field( - default_factory=lambda: os.environ.get('PIP_CHECK_CONFLICTS', json.dumps(True)) in [json.dumps(True)], + default_factory=lambda: os.environ.get( + 'PIP_CHECK_CONFLICTS', json.dumps(True) + ) + in [json.dumps(True)], ) uv_cache_dir: str = dataclasses.field( default_factory=lambda: os.environ.get( @@ -438,7 +459,9 @@ def requirements_name_get( else: requirements_path = source_dir / 'requirements.txt' - requirements_path_in = requirements_path.parent / (requirements_path.stem + '.in') + requirements_path_in = requirements_path.parent / ( + requirements_path.stem + '.in' + ) requirements_in: list[str] = [] @@ -594,10 +617,15 @@ def env_bootstrap( requirements_in: list[str] = [] - requirements_in.extend(['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11']) + requirements_in.extend( + ['uv', 'pip', 'build', 'setuptools', 'meson-python', 'pybind11'] + ) if pyproject.early_features: - early_dependencies = sum([pyproject.dependencies[o] for o in pyproject.early_features], cast(list[str], [])) + early_dependencies = sum( + [pyproject.dependencies[o] for o in pyproject.early_features], + cast(list[str], []), + ) logger.info( dict( @@ -647,14 +675,6 @@ def env_bootstrap( needs_compile = True constraint_args = ['-c', str(requirements_path)] - if (not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update) and requirements_path.exists(): - whl_cache_download( - whl_cache_path=bootstrap_settings.whl_cache_path, - requirements_path=requirements_path, - uv_python_version=uv_python_version, - pip_find_links_args=pip_find_links_args, - ) - cache_find_links_args: list[str] = [] if bootstrap_settings.whl_cache_path.exists(): cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] @@ -711,15 +731,15 @@ def env_bootstrap( os.unlink(f_out.name) raise - if not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update: - whl_cache_download( - whl_cache_path=bootstrap_settings.whl_cache_path, - requirements_path=requirements_path, - uv_python_version=uv_python_version, - pip_find_links_args=pip_find_links_args, - ) - if bootstrap_settings.whl_cache_path.exists(): - cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] + if not bootstrap_settings.whl_cache_path.exists() or bootstrap_settings.whl_cache_update: + whl_cache_download( + whl_cache_path=bootstrap_settings.whl_cache_path, + requirements_path=requirements_path, + uv_python_version=uv_python_version, + pip_find_links_args=pip_find_links_args, + ) + if bootstrap_settings.whl_cache_path.exists(): + cache_find_links_args = ['-f', str(bootstrap_settings.whl_cache_path)] if bootstrap_settings.venv_partial and bootstrap_settings.env_path.exists(): logger.info('[bootstrap] VENV_PARTIAL: skipping venv creation (already exists)') diff --git a/python/online/fxreader/pr34/commands_typed/color_scheme.py b/python/online/fxreader/pr34/commands_typed/color_scheme.py new file mode 100644 index 0000000..13106f5 --- /dev/null +++ b/python/online/fxreader/pr34/commands_typed/color_scheme.py @@ -0,0 +1,109 @@ +import subprocess +import sys +import json +import logging +from typing import ( + Literal, + Optional, +) + +import argparse + +logger = logging.getLogger(__name__) + + +def run(argv: list[str]) -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + 'action', + choices=[ + 'toggle', + 'dark', + 'light', + 'get', + ], + # required=True, + type=str, + help='action', + ) + from .argparse import parse_args as pr34_parse_args + + options, args = pr34_parse_args(parser, argv) + assert len(args) == 0 + + def get_theme() -> Literal['light', 'dark', 'default']: + res = ( + subprocess.check_output( + [ + 'gsettings', + 'get', + 'org.gnome.desktop.interface', + 'color-scheme', + ] + ) + .decode('utf-8') + .strip() + ) + + if res == "'prefer-dark'": + return 'dark' + elif res == "'prefer-light'": + return 'light' + elif res == "'default'": + return 'default' + else: + logger.error(dict(res=res, msg='unknown theme')) + + raise NotImplementedError + + def set_theme(theme: Literal['light', 'dark', 'default']) -> None: + if theme == 'light': + subprocess.check_call( + [ + 'gsettings', + 'set', + 'org.gnome.desktop.interface', + 'color-scheme', + 'prefer-light', + ] + ) + elif theme == 'dark': + subprocess.check_call( + [ + 'gsettings', + 'set', + 'org.gnome.desktop.interface', + 'color-scheme', + 'prefer-dark', + ] + ) + elif theme == 'default': + subprocess.check_call( + [ + 'gsettings', + 'reset', + 'org.gnome.desktop.interface', + 'color-scheme', + ] + ) + else: + raise NotImplementedError + + def toggle() -> None: + theme = get_theme() + if theme in ('light', 'default'): + set_theme('dark') + else: + set_theme('light') + + if options.action == 'toggle': + toggle() + elif options.action == 'dark': + set_theme('dark') + elif options.action == 'light': + set_theme('light') + elif options.action == 'get': + sys.stdout.write(json.dumps(get_theme())) + sys.stdout.flush() + else: + raise NotImplementedError diff --git a/python/online/fxreader/pr34/commands_typed/crypto.py b/python/online/fxreader/pr34/commands_typed/crypto.py index 327d1cb..aeac872 100644 --- a/python/online/fxreader/pr34/commands_typed/crypto.py +++ b/python/online/fxreader/pr34/commands_typed/crypto.py @@ -64,7 +64,9 @@ class PasswordUtils: raise NotImplementedError @classmethod - def _scrypt_init(cls, salt: bytes) -> cryptography.hazmat.primitives.kdf.scrypt.Scrypt: + def _scrypt_init( + cls, salt: bytes + ) -> cryptography.hazmat.primitives.kdf.scrypt.Scrypt: return cryptography.hazmat.primitives.kdf.scrypt.Scrypt( salt=salt, length=32, diff --git a/python/online/fxreader/pr34/commands_typed/logging.py b/python/online/fxreader/pr34/commands_typed/logging.py index afdd4c1..abc4bf8 100644 --- a/python/online/fxreader/pr34/commands_typed/logging.py +++ b/python/online/fxreader/pr34/commands_typed/logging.py @@ -10,5 +10,7 @@ def setup(level: Optional[int] = None) -> None: logging.basicConfig( level=level, - format=('%(levelname)s:%(name)s:%(message)s:%(process)d:%(asctime)s:%(pathname)s:%(funcName)s:%(lineno)s'), + format=( + '%(levelname)s:%(name)s:%(message)s:%(process)d:%(asctime)s:%(pathname)s:%(funcName)s:%(lineno)s' + ), ) diff --git a/python/online/fxreader/pr34/commands_typed/metrics.py b/python/online/fxreader/pr34/commands_typed/metrics.py new file mode 100644 index 0000000..c98cae1 --- /dev/null +++ b/python/online/fxreader/pr34/commands_typed/metrics.py @@ -0,0 +1,121 @@ +import pydantic +import json +import logging +import datetime + +# import django.http + +from typing import ( + Literal, + Any, + Optional, + Annotated, + cast, + TypeVar, + Protocol, + Generic, + Callable, +) + + +logger = logging.getLogger(__name__) + + +class Metric(pydantic.BaseModel): + name: str + type: Literal['gauge', 'counter'] + help: Optional[str] = None + + class Sample(pydantic.BaseModel): + value: str + parameters: dict[str, str] + timestamp: Optional[datetime.datetime] = None + + samples: list[Sample] = pydantic.Field( + default_factory=lambda: [], + ) + + @classmethod + def sample_serialize( + cls, + o: 'Metric', + s: 'Metric.Sample', + ) -> str: + samples: list[Metric.Sample] = [ + s, + ] + + if o.type == 'gauge': + samples.append( + Metric.Sample( + parameters=s.parameters, + value='NaN', + timestamp=( + s.timestamp + datetime.timedelta(seconds=15) + if s.timestamp + else None + ), + ) + ) + + return ''.join( + [ + '{metric}{{{parameters}}} {value} {timestamp}\n'.format( + metric=o.name, + parameters=','.join( + [ + '%s=%s' + % ( + k, + json.dumps(v), + ) + for k, v in s2.parameters.items() + ] + ), + value=s2.value, + timestamp=( + '%.f' % (s2.timestamp.timestamp() * 1000,) + if s2.timestamp + else '' + ), + ) + for s2 in samples + ] + ) + + +class serialize_t: + class res_t(pydantic.BaseModel): + json2: str + content_type: str + + +def serialize( + metrics: list[Metric], +): + return serialize_t.res_t( + json2=''.join( + [ + '{help}{type}{samples}'.format( + # help='# HELP %s some metric' % o.name, + # type='# TYPE %s counter' % o.name, + help=( + '# HELP {0} {1}\n'.format(o.name, o.help) + if o.help + else '' + ), + type=( + '# TYPE {0} {1}\n'.format(o.name, o.type) + if o.type + else '' + ), + samples=''.join( + [Metric.sample_serialize(o, s) for s in o.samples] + ), + ) + for o in metrics + if len(o.samples) > 0 + ] + ), + content_type='text/plain; version=0.0.4; charset=utf-8', + ) diff --git a/python/online/fxreader/pr34/commands_typed/mypy.py b/python/online/fxreader/pr34/commands_typed/mypy.py index 82bb7d8..91de680 100644 --- a/python/online/fxreader/pr34/commands_typed/mypy.py +++ b/python/online/fxreader/pr34/commands_typed/mypy.py @@ -38,7 +38,9 @@ class MypyFormatEntry: class MypyFormat: - vscode: ClassVar[MypyFormatEntry] = MypyFormatEntry(name='vscode', value='vscode') + vscode: ClassVar[MypyFormatEntry] = MypyFormatEntry( + name='vscode', value='vscode' + ) json: ClassVar[MypyFormatEntry] = MypyFormatEntry(name='json', value='json') @classmethod @@ -149,7 +151,11 @@ def run( assert not res.returncode is None errors = sorted( - [json.loads(o) for o in res.stdout.decode('utf-8').splitlines() if not o.strip() == ''], + [ + json.loads(o) + for o in res.stdout.decode('utf-8').splitlines() + if not o.strip() == '' + ], key=lambda x: ( x.get('file', ''), x.get('line', 0), diff --git a/python/online/fxreader/pr34/commands_typed/os.py b/python/online/fxreader/pr34/commands_typed/os.py index 4dc2b9b..13a646d 100644 --- a/python/online/fxreader/pr34/commands_typed/os.py +++ b/python/online/fxreader/pr34/commands_typed/os.py @@ -54,8 +54,21 @@ def runtime_libdirs_init( ld_library_path: list[pathlib.Path] = [ o for o in [ - *[o.absolute() for o in (project.runtime_libdirs if project.runtime_libdirs else [])], - *[pathlib.Path(o) for o in os.environ.get('LD_LIBRARY_PATH', '').split(os.path.pathsep) if o != ''], + *[ + o.absolute() + for o in ( + project.runtime_libdirs + if project.runtime_libdirs + else [] + ) + ], + *[ + pathlib.Path(o) + for o in os.environ.get('LD_LIBRARY_PATH', '').split( + os.path.pathsep + ) + if o != '' + ], ] ] @@ -72,10 +85,16 @@ def runtime_libdirs_init( ld_library_path_present.append(o) - os.environ.update(LD_LIBRARY_PATH=os.path.pathsep.join([str(o) for o in ld_library_path_present])) + os.environ.update( + LD_LIBRARY_PATH=os.path.pathsep.join( + [str(o) for o in ld_library_path_present] + ) + ) for preload_path in project.runtime_preload or []: - for preload_found in glob.glob(str(preload_path.parent / ('lib%s.so' % preload_path.name))): + for preload_found in glob.glob( + str(preload_path.parent / ('lib%s.so' % preload_path.name)) + ): logger.info( dict( preload_path=preload_path, diff --git a/python/online/fxreader/pr34/commands_typed/pip.py b/python/online/fxreader/pr34/commands_typed/pip.py index 2849076..df69eb8 100644 --- a/python/online/fxreader/pr34/commands_typed/pip.py +++ b/python/online/fxreader/pr34/commands_typed/pip.py @@ -101,8 +101,20 @@ class pip_resolve_t: entries: Optional[list[download_info_t]] = None -def pip_resolve_entries_to_txt(entries: list[pip_resolve_t.res_t.download_info_t]) -> str: - return '\n'.join(['#%s\n%s %s' % (o.url, o.constraint, ' '.join(['--hash=sha256:%s' % o2 for o2 in o.sha256])) for o in entries]) +def pip_resolve_entries_to_txt( + entries: list[pip_resolve_t.res_t.download_info_t], +) -> str: + return '\n'.join( + [ + '#%s\n%s %s' + % ( + o.url, + o.constraint, + ' '.join(['--hash=sha256:%s' % o2 for o2 in o.sha256]), + ) + for o in entries + ] + ) def pip_resolve( @@ -128,7 +140,9 @@ def pip_resolve( import pip._internal.models.direct_url with contextlib.ExitStack() as stack: - stack.enter_context(pip._internal.utils.temp_dir.global_tempdir_manager()) + stack.enter_context( + pip._internal.utils.temp_dir.global_tempdir_manager() + ) t2 = pip._internal.cli.main_parser.create_main_parser() @@ -166,15 +180,22 @@ def pip_resolve( pip._internal.cli.cmdoptions.check_dist_restriction(options) # t1._in_main_context = True session = t1.get_default_session(options) - target_python = pip._internal.cli.cmdoptions.make_target_python(options) - finder = cast(pip_resolve_t.build_package_finder_t, getattr(t1, '_build_package_finder'))( + target_python = pip._internal.cli.cmdoptions.make_target_python( + options + ) + finder = cast( + pip_resolve_t.build_package_finder_t, + getattr(t1, '_build_package_finder'), + )( options=options, session=session, target_python=target_python, ignore_requires_python=options.ignore_requires_python, ) - build_tracker = t1.enter_context(pip._internal.operations.build.build_tracker.get_build_tracker()) + build_tracker = t1.enter_context( + pip._internal.operations.build.build_tracker.get_build_tracker() + ) reqs = t1.get_requirements( [ #'pip', 'uv', 'ipython', @@ -184,8 +205,12 @@ def pip_resolve( finder, session, ) - pip._internal.req.req_install.check_legacy_setup_py_options(options, reqs) - directory = pip._internal.utils.temp_dir.TempDirectory(delete=True, kind='download', globally_managed=True) + pip._internal.req.req_install.check_legacy_setup_py_options( + options, reqs + ) + directory = pip._internal.utils.temp_dir.TempDirectory( + delete=True, kind='download', globally_managed=True + ) preparer = t1.make_requirement_preparer( temp_build_dir=directory, options=options, @@ -205,7 +230,9 @@ def pip_resolve( py_version_info=options.python_version, ) t1.trace_basic_info(finder) - requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) res = pip_resolve_t.res_t() @@ -279,7 +306,9 @@ def pip_resolve( location, ) - batch_downloader_call_def = pip._internal.network.download.BatchDownloader.__call__ + batch_downloader_call_def = ( + pip._internal.network.download.BatchDownloader.__call__ + ) def batch_downloader_call( _self: pip._internal.network.download.BatchDownloader, @@ -298,7 +327,9 @@ def pip_resolve( return [(o, ('/dev/null', '')) for o in links] # base_resolver_resolve_def = pip._internal.resolution.base.BaseResolver.resolve - base_resolver_resolve_def = pip._internal.resolution.resolvelib.resolver.Resolver.resolve + base_resolver_resolve_def = ( + pip._internal.resolution.resolvelib.resolver.Resolver.resolve + ) result_requirements: list[RequirementSet | InstallRequirement] = [] @@ -309,7 +340,9 @@ def pip_resolve( ) -> RequirementSet: # print(args, kwargs) - res = base_resolver_resolve_def(_self, root_reqs, check_supported_wheels) + res = base_resolver_resolve_def( + _self, root_reqs, check_supported_wheels + ) result_requirements.append(res) raise NotImplementedError @@ -369,7 +402,13 @@ def pip_resolve( patches: list[Any] = [] - patches.append(unittest.mock.patch.object(pip._internal.network.download.Downloader, '__call__', downloader_call)) + patches.append( + unittest.mock.patch.object( + pip._internal.network.download.Downloader, + '__call__', + downloader_call, + ) + ) # patches.append( # unittest.mock.patch.object( # pip._internal.network.download.BatchDownloader, @@ -575,4 +614,6 @@ def pip_check_conflicts( if line.strip() != '' ] - return pip_check_conflicts_t.res_t(status=('error' if len(duplicates) > 0 else 'ok'), duplicates=duplicates) + return pip_check_conflicts_t.res_t( + status=('error' if len(duplicates) > 0 else 'ok'), duplicates=duplicates + ) diff --git a/python/online/fxreader/pr34/commands_typed/pydantic.py b/python/online/fxreader/pr34/commands_typed/pydantic.py index 07e1d79..6d86c9c 100644 --- a/python/online/fxreader/pr34/commands_typed/pydantic.py +++ b/python/online/fxreader/pr34/commands_typed/pydantic.py @@ -1,4 +1,7 @@ import pydantic +import functools + +# import asgiref.sync import inspect import collections @@ -9,27 +12,45 @@ from typing import ( Optional, Mapping, cast, + Awaitable, + overload, ) P = TypeVar('P') R = TypeVar('R') -def validate_params(view: Callable[..., R]) -> Callable[..., R]: +@overload +def validate_params( + view: Callable[..., Awaitable[R]], +) -> Callable[..., Awaitable[R]]: ... + + +@overload +def validate_params(view: Callable[..., R]) -> Callable[..., R]: ... + + +def validate_params( + view: Callable[..., Awaitable[R]] | Callable[..., R], +) -> Callable[..., Awaitable[R]] | Callable[..., R]: class Parameter: kind: Any annotation: Any - parameters = cast(Mapping[str, Parameter], inspect.signature(view).parameters) + parameters = cast( + Mapping[str, Parameter], inspect.signature(view).parameters + ) - positional_parameters: collections.OrderedDict[str, type[Any]] = collections.OrderedDict( - ( - (k, v.annotation) - for k, v in parameters.items() - if v.kind - in ( - inspect.Parameter.POSITIONAL_ONLY, - inspect.Parameter.POSITIONAL_OR_KEYWORD, + positional_parameters: collections.OrderedDict[str, type[Any]] = ( + collections.OrderedDict( + ( + (k, v.annotation) + for k, v in parameters.items() + if v.kind + in ( + inspect.Parameter.POSITIONAL_ONLY, + inspect.Parameter.POSITIONAL_OR_KEYWORD, + ) ) ) ) @@ -52,7 +73,10 @@ def validate_params(view: Callable[..., R]) -> Callable[..., R]: ), ) - def wrapper(*args: Any, **kwargs: Any) -> R: + sync_view: Optional[Callable[..., R]] = None + async_view: Optional[Callable[..., Awaitable[R]]] = None + + def validate_params(*args: Any, **kwargs: Any) -> None: # data = model.model_validate( kwargs_to_check: dict[str, Any] = {k: v for k, v in kwargs.items()} @@ -71,10 +95,23 @@ def validate_params(view: Callable[..., R]) -> Callable[..., R]: ) # ).dict() - return view( - # **data, - *args, - **kwargs, - ) + if inspect.iscoroutinefunction(view): + async_view = cast(Callable[..., Awaitable[R]], view) - return wrapper + @functools.wraps(async_view) + async def async_wrapper(*args: Any, **kwargs: Any) -> R: + validate_params(*args, **kwargs) + + return await async_view(*args, **kwargs) + + return async_wrapper + else: + sync_view = cast(Callable[..., R], view) + + @functools.wraps(sync_view) + def wrapper(*args: Any, **kwargs: Any) -> R: + validate_params(*args, **kwargs) + + return sync_view(*args, **kwargs) + + return wrapper diff --git a/python/online/fxreader/pr34/commands_typed/status.py b/python/online/fxreader/pr34/commands_typed/status.py new file mode 100644 index 0000000..0569af8 --- /dev/null +++ b/python/online/fxreader/pr34/commands_typed/status.py @@ -0,0 +1,228 @@ +import sys +import datetime +import time +import os +import signal +import io +import json +import subprocess +import logging +import inspect +import textwrap +import optparse +import traceback + +from . import logging as pr34_logging + +from typing import ( + Any, + Optional, +) + +logger = logging.getLogger(__name__) + + +def get_info( + sh: list[str], + timeout: int | float, +): + t1: list[str] = [] + + for sh_index, o in enumerate( + [ + *sh, + *[ + r""" + A=$(free -h | grep -P Mem: | grep -Po '[\w\.\d]+'); + echo -n $A | awk '{print $2, $7}'; + """, + r""" + date +'%Y-%m-%d %l:%M:%S %p %Z'; + """, + ], + ] + ): + try: + t1.append( + subprocess.check_output( + o, + shell=True, + timeout=timeout, + ) + .decode('utf-8') + .strip() + ) + except Exception: + t1.append('fail %d' % sh_index) + + t3 = ' | '.join(t1).replace('\n\r', '') + + sys.stdout.write(t3) + sys.stdout.flush() + + +def run(argv: list[str]): + # logging.basicConfig(level=logging.INFO) + pr34_logging.setup() + + assert isinstance(argv, list) and all([isinstance(o, str) for o in argv]) + + class c1(optparse.IndentedHelpFormatter): + def format_option(self, *args: Any, **kwargs: Any) -> Any: + def f1(text: str, width: int) -> list[str]: + # width = None + return '\n'.join( + [ + textwrap.fill('\t' + o, width, replace_whitespace=False) + for o in text.splitlines() + ] + ).splitlines() + + t1 = inspect.getsource(optparse.IndentedHelpFormatter.format_option) + t2 = ( + '\n'.join([o[4:] for o in t1.splitlines()[:]]) + .replace( + 'textwrap.wrap', + 'f1', + ) + .replace('format_option', 'f2') + ) + ns: dict[str, Any] = dict() + exec(t2, dict(f1=f1), ns) + return ns['f2'](self, *args, **kwargs) + + parser = optparse.OptionParser( + formatter=c1( + width=int(os.environ.get('COLUMNS', '9999999')), + ), + ) + + def add_option( + p: optparse.OptionParser, + option_name: str, + dest: str, + default: Optional[Any] = None, + action: Optional[str] = None, + **kwargs: Any, + ) -> None: + getattr(p, 'add_option')( + option_name, + dest=dest, + default=default, + action=action, + **kwargs, + ) + + add_option( + parser, + '--sh', + dest='sh', + default=[], + action='append', + type=str, + ) + add_option( + parser, + '--timeout', + dest='timeout', + default=None, + type=float, + ) + add_option( + parser, + '--repeat_interval', + dest='repeat_interval', + default=None, + type=float, + ) + add_option( + parser, + '--config', + dest='config', + default=None, + type=str, + help=''.join( + [ + '.json file with array of strings, each is a shell command ', + 'that outputs a separate status text value, ', + 'like\n', + r""" +ping -w 1 -i 0.02 -c 3 | tail -n 2| head -n 1 | grep -Po $'time\\s+.*$' +sensors -j | jq -r '.\"coretemp-isa-0000\".\"Package id 0\".temp1_input|tostring + \" C\"' +printf '%d RPM' $(cat /sys/devices/platform/applesmc.768/fan1_input) +printf '% 3.0f%%' $(upower -d | grep -Po 'percentage:\\s+\\d+(\\.\\d+)?%' | grep -Po '\\d+(\\.\\d+)?' | head -n 1) + """.strip(), + ] + ), + ) + options, args = parser.parse_args(argv) + + if options.timeout is None: + options.timeout = 0.5 + + timeout2 = max(options.timeout, 0.0) + + assert timeout2 >= 0.0 and timeout2 <= 4 + + config: dict[str, Any] = dict() + + try: + if not options.config is None: + with io.open(options.config, 'r') as f: + config.update(json.load(f)) + except Exception: + logging.error(traceback.format_exc()) + pass + + options.sh.extend(config.get('sh', [])) + + last_ts = datetime.datetime.now() + + shutdown: bool = False + + def on_signal(*args: Any, **kwargs: Any): + nonlocal shutdown + + shutdown = True + + signal.signal(signal.SIGINT, on_signal) + signal.signal(signal.SIGTERM, on_signal) + + while not shutdown: + get_info( + timeout=timeout2, + sh=options.sh, + ) + + if not options.repeat_interval: + break + else: + sys.stdout.write('\n') + sys.stdout.flush() + + is_late = False + + new_ts = last_ts + + while True: + now_ts = datetime.datetime.now() + spent = (now_ts - last_ts).total_seconds() + + new_ts = last_ts + datetime.timedelta( + seconds=options.repeat_interval + ) + + if new_ts > now_ts: + if is_late: + last_ts = new_ts + break + else: + last_ts = new_ts + is_late = True + + if spent < options.repeat_interval: + time.sleep(options.repeat_interval - spent) + + +if __name__ == '__main__': + run(sys.argv[1:]) diff --git a/python/online/fxreader/pr34/oom_firefox.py b/python/online/fxreader/pr34/oom_firefox.py new file mode 100644 index 0000000..674105e --- /dev/null +++ b/python/online/fxreader/pr34/oom_firefox.py @@ -0,0 +1,724 @@ +#!/usr/bin/env python3 +import psutil +import pathlib +import logging +import logging.handlers +import os +import signal +import subprocess +import threading +import time +import argparse +import re +import sys + +from prompt_toolkit.application import Application +from prompt_toolkit.key_binding import KeyBindings, ConditionalKeyBindings +from prompt_toolkit.layout import Layout, HSplit, FloatContainer, Float +from prompt_toolkit.filters import Condition +from prompt_toolkit.layout.containers import Window +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.widgets import TextArea, Frame, Dialog, Button, Label +from prompt_toolkit.styles import Style + +from typing import ( + TypedDict, + Any, + Optional, +) +from collections import OrderedDict + +logger = logging.getLogger(__name__) + +__version__ = '0.6.1' +__created__ = '2025-11-21' + +# — Helper for cgroup / slice matching — + + +def get_cgroup_path(pid: int) -> Optional[str]: + try: + with open(f'/proc/{pid}/cgroup', 'r') as f: + for line in f: + parts = line.strip().split(':', 2) + if len(parts) == 3: + return parts[2] + except Exception: + logger.exception('') + return None + return None + + +def slice_matches(cpath, target_slice): + if not cpath or not target_slice: + return False + comps = cpath.strip('/').split('/') + tgt = target_slice.lower() + for comp in comps: + name = comp.lower() + if name.endswith('.slice'): + name = name[:-6] + if tgt == name: + return True + return False + + +# — Memory‑management logic — + + +class get_firefox_procs_ps_t: + class res_t: + class entry_t(TypedDict): + rss: int + pid: int + ppid: int + cgroup: str + cmd: str + + +def get_firefox_procs_ps( + slice_name=None, +) -> list[get_firefox_procs_ps_t.res_t.entry_t]: + entries: dict[int, dict[str, Any]] = dict() + + for regex, columns in [ + ( + re.compile(r'^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.*)$'), + OrderedDict( + pid=lambda x: int(x[1]), + rss=lambda x: int(x[2]) * 1024, + ppid=lambda x: int(x[3]), + cmd=lambda x: x[4], + ), + ), + ( + re.compile(r'^\s*(\d+)\s+(.*)$'), + OrderedDict( + pid=lambda x: int(x[1]), + cgroup=lambda x: x[2], + ), + ), + ]: + lines = ( + subprocess.check_output( + [ + 'ps', + '-ax', + '-o', + ','.join(columns.keys()), + ] + ) + .decode('utf-8') + .splitlines()[1:] + ) + + for line in lines: + r = re.compile(regex) + # print([r, line]) + match = r.match(line) + + assert match + + entry = {k: v(match) for k, v in columns.items()} + + if not entry['pid'] in entries: + entries[entry['pid']] = dict() + + entries[entry['pid']].update(entry) + + filtered_entries: list[dict[str, Any]] = [] + + for entry in entries.values(): + if not 'cgroup' in entry or not 'rss' in entry: + continue + + if not slice_name is None: + if not slice_name in entry['cgroup']: + continue + filtered_entries.append(entry) + + return filtered_entries + + +def get_firefox_procs(slice_name=None): + procs = [] + for p in psutil.process_iter(['pid', 'name', 'cmdline', 'memory_info']): + try: + name = p.info['name'] + cmd = p.info['cmdline'] + if not cmd: + continue + if 'firefox' not in name and not (cmd and 'firefox' in cmd[0]): + continue + if slice_name: + cpath = get_cgroup_path(p.pid) + if not slice_matches(cpath, slice_name): + continue + procs.append(p) + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + return procs + + +def total_rss_mb(procs: list['get_firefox_procs_ps_t.res_t.entry_t']): + total = 0 + for p in procs: + try: + # total += p.memory_info().rss + total += p['rss'] + except Exception: + logger.exception('') + pass + return total / (1024 * 1024) + + +def is_main_firefox(p): + try: + # for arg in p.cmdline(): + # for arg in p['cmd'].split(): + # if "contentproc" in arg: + # return False + if 'contentproc' in p['cmd']: + return False + return True + except Exception: + logger.exception('') + return False + + +def kill_prioritized( + procs: list['get_firefox_procs_ps_t.res_t.entry_t'], + to_free_mb, + low_priority_pids, + main_regex: Optional[str], + worker_regex: Optional[str], +): + candidates = [] + for p in procs: + if worker_regex is None and main_regex is None: + pass + elif ( + not worker_regex is None + and not re.compile(worker_regex).match(p['cmd']) is None + ): + pass + elif ( + not main_regex is None + and not re.compile(main_regex).match(p['cmd']) is None + ): + continue + elif not main_regex is None and not worker_regex is None: + continue + elif main_regex is None and not worker_regex is None: + continue + elif not main_regex is None and worker_regex is None: + pass + else: + raise NotImplementedError + + try: + # rss_mb = p.memory_info().rss / (1024 * 1024) + rss_mb = p['rss'] / (1024 * 1024) + candidates.append((p, rss_mb)) + except Exception: + logger.exception('') + continue + + candidates.sort(key=lambda x: ((x[0]['pid'] in low_priority_pids), -x[1])) + + freed = 0.0 + killed = [] + for p, rss in candidates: + if freed >= to_free_mb: + break + + logger.info( + dict( + p=p, + action='kill', + msg='started', + to_free_mb=to_free_mb, + killed=killed, + freed=freed, + low_priority_pids=low_priority_pids, + ) + ) + + try: + os.kill(p['pid'], signal.SIGTERM) + killed.append(p['pid']) + freed += rss + except Exception as e: + logger.exception(f'Error killing pid {p["pid"]}') + # print(f"Error killing pid {p.pid}: {e}", file=sys.stderr) + return killed, freed + + +# — systemd-run logic — + + +def launch_firefox_with_limits( + base_cmd, memory_high, swap_max, extra_args, unit_name +): + cmd = [ + 'systemd-run', + '--user', + '--scope', + '-p', + f'MemoryHigh={int(memory_high)}M', + ] + if swap_max is not None: + cmd += ['-p', f'MemorySwapMax={int(swap_max)}M'] + if unit_name: + cmd += ['--unit', unit_name] + + cmd += base_cmd + cmd += extra_args + + devnull = subprocess.DEVNULL + proc = subprocess.Popen(cmd, stdin=devnull, stdout=devnull, stderr=devnull) + print('Launched Firefox via systemd-run, PID:', proc.pid, file=sys.stderr) + return proc + + +# — Main + TUI + Monitoring — + + +def main(): + parser = argparse.ArgumentParser( + description='Firefox memory manager with slice + graceful shutdown' + ) + parser.add_argument( + '--max-mb', + type=float, + required=True, + help='Memory threshold in MB (used for killing logic & MemoryHigh)', + ) + parser.add_argument( + '--kill-percent', + type=float, + default=70.0, + help='If over max, kill until usage ≤ this percent of max', + ) + parser.add_argument( + '--swap-max-mb', + type=float, + default=None, + help='MemorySwapMax (MB) for the systemd scope', + ) + parser.add_argument( + '--interval', + type=float, + default=1.0, + help='Monitoring interval in seconds', + ) + parser.add_argument( + '--unit-name', + type=str, + default='firefox-limited', + help='Name for systemd transient unit', + ) + parser.add_argument( + '--main-regex', + type=str, + default=None, + help='regex for main processes, that are not to kill', + ) + parser.add_argument( + '--worker-regex', + type=str, + # default=r'^.*contentproc.*$', + default=None, + help='regex for worker processes, that can be killed, like .*contentproc.* for firefox', + ) + parser.add_argument( + '--firefox-extra', + action='append', + default=[], + help='Extra CLI args to pass to Firefox (can repeat)', + ) + parser.add_argument( + 'firefox_cmd', + nargs=argparse.REMAINDER, + help='Firefox command + args (if launching it)', + ) + + args = parser.parse_args() + + os.makedirs( + pathlib.Path('~/.cache/oom_firefox/').expanduser(), exist_ok=True + ) + + logging.basicConfig( + level=logging.INFO, + format=( + '%(asctime)s ' + '%(levelname)-8s ' + '%(filename)s:%(lineno)d ' + '%(funcName)s – %(message)s' + ), + handlers=[ + logging.handlers.RotatingFileHandler( + pathlib.Path( + '~/.cache/oom_firefox/log-%s' % args.unit_name + ).expanduser(), + maxBytes=128 * 1024, + backupCount=3, + ) + ], + ) + + low_priority_pids = set() + body = TextArea(focusable=False, scrollbar=True) + + terminate_flag = threading.Event() + + lock = threading.Lock() + + firefox_proc = None + + def terminate(): + terminate_flag.set() + app.exit() + + def stop(): + with lock: + if firefox_proc: + try: + firefox_proc.terminate() + firefox_proc.wait(timeout=5) + except Exception: + logger.exception('') + try: + firefox_proc.kill() + except Exception: + logger.exception('') + pass + # app.exit() + + signal.signal(signal.SIGINT, lambda s, f: terminate()) + signal.signal(signal.SIGTERM, lambda s, f: terminate()) + + def refresh_body(): + nonlocal firefox_proc + + with lock: + procs = get_firefox_procs_ps(slice_name=args.unit_name) + total = total_rss_mb(procs) + limit = args.max_mb + kill_to = args.kill_percent / 100.0 * limit + + lines = [ + f'Firefox RSS (slice={args.unit_name}): {total:.1f} MB', + f'Threshold (max): {limit:.1f} MB', + f'Kill‑to target: {kill_to:.1f} MB ({args.kill_percent}%)', + f'Low‑priority PIDs: {sorted(low_priority_pids)}', + ] + + if total > limit: + to_free = total - kill_to + + logger.info( + dict( + total=total, + limit=limit, + kill_to=kill_to, + to_free=to_free, + low_priority_pids=low_priority_pids, + worker_regex=args.worker_regex, + main_regex=args.main_regex, + ) + ) + + killed, freed = kill_prioritized( + procs, + to_free, + low_priority_pids, + main_regex=args.main_regex, + worker_regex=args.worker_regex, + ) + lines.append(f'Killed: {killed}') + lines.append(f'Freed ≈ {freed:.1f} MB') + else: + lines.append('Within limit — no kill') + + if firefox_proc and firefox_proc.poll() is not None: + print('Firefox died — restarting …', file=sys.stderr) + firefox_proc = launch_firefox_with_limits( + args.firefox_cmd, + memory_high=args.max_mb, + swap_max=args.swap_max_mb, + extra_args=args.firefox_extra, + unit_name=args.unit_name, + ) + + body.text = '\n'.join(lines) + + dialog_float = [None] + root_floats = [] + + def open_pid_dialog(): + ta = TextArea(text='', multiline=True, scrollbar=True) + + def on_ok(): + txt = ta.text + for m in re.finditer(r'\((\d+)[^\)\d]*\)', txt): + low_priority_pids.add(int(m.group(1))) + for m in re.finditer(r'^\s*(\d+)\s*$', txt): + low_priority_pids.add(int(m.group(1))) + for m in re.finditer(r'^\s*-(\d+)\s*$', txt): + low_priority_pids.remove(int(m.group(1))) + close_dialog() + refresh_body() + + def on_cancel(): + close_dialog() + + dialog = Dialog( + title='Enter low‑priority PIDs', + body=ta, + buttons=[ + Button(text='OK', handler=on_ok), + Button(text='Cancel', handler=on_cancel), + ], + width=60, + modal=True, + ) + f = Float(content=dialog, left=2, top=2) + dialog_float[0] = f + root_floats.append(f) + app.layout.focus(ta) + + def change_kill_percent(kill_percent: float) -> None: + assert kill_percent >= 10 and kill_percent <= 90 + + args.kill_percent = kill_percent + + def change_max_mb(max_mb: int) -> None: + for cmd in ( + [ + 'systemctl', + '--user', + 'set-property', + '%s.scope' % args.unit_name, + 'MemoryHigh=%dM' % max_mb, + ], + [ + 'systemctl', + '--user', + 'set-property', + '%s.scope' % args.unit_name, + 'MemoryMax=%dM' % (max_mb * 1.1), + ], + ): + logger.info(dict(cmd=cmd)) + + subprocess.check_call(cmd) + + args.max_mb = max_mb + + def open_limit_dialog(): + ta = TextArea(text='', multiline=True, scrollbar=True) + + def on_ok(): + txt = ta.text + m = re.compile(r'^\s*(\d+)\s*$').match(txt) + + if m: + change_max_mb(int(m[1])) + + close_dialog() + refresh_body() + else: + logger.error('invalid input %s' % txt) + + def on_cancel(): + close_dialog() + + dialog = Dialog( + title='Enter maximum memory threshold in MB', + body=ta, + buttons=[ + Button(text='OK', handler=on_ok), + Button(text='Cancel', handler=on_cancel), + ], + width=60, + modal=True, + ) + f = Float(content=dialog, left=2, top=2) + dialog_float[0] = f + root_floats.append(f) + app.layout.focus(ta) + + def open_percentage_dialog(): + ta = TextArea(text='', multiline=True, scrollbar=True) + + def on_ok(): + txt = ta.text + m = re.compile(r'^\s*(\d+|\d+\.\d+)\s*$').match(txt) + + if m: + change_kill_percent(float(m[1])) + + close_dialog() + refresh_body() + else: + logger.error('invalid input %s' % txt) + + def on_cancel(): + close_dialog() + + dialog = Dialog( + title='Enter kill percent from 10% to 90%, without % sign', + body=ta, + buttons=[ + Button(text='OK', handler=on_ok), + Button(text='Cancel', handler=on_cancel), + ], + width=60, + modal=True, + ) + f = Float(content=dialog, left=2, top=2) + dialog_float[0] = f + root_floats.append(f) + app.layout.focus(ta) + + def open_message(title, message): + def on_close(): + close_dialog() + + dialog = Dialog( + title=title, + body=Label(text=message), + buttons=[Button(text='Close', handler=on_close)], + width=50, + modal=True, + ) + f = Float(content=dialog, left=4, top=4) + dialog_float[0] = f + root_floats.append(f) + app.layout.focus(dialog) + + def close_dialog(): + f = dialog_float[0] + if f in root_floats: + root_floats.remove(f) + dialog_float[0] = None + app.layout.focus(body) + + kb = KeyBindings() + + gkb = ConditionalKeyBindings( + key_bindings=kb, + filter=Condition(lambda: dialog_float[0] is None), + ) + + @kb.add('q') + def _(event): + terminate() + + @kb.add('m') + def _(event): + open_pid_dialog() + + @kb.add('l') + def _(event): + open_limit_dialog() + + @kb.add('p') + def _(event): + open_percentage_dialog() + + HELP_TEXT = 'm=add PIDs, l=change limit, s=settings, a=about, q=quit' + + @kb.add('h') + def _(event): + open_message( + 'Help', + 'Keys: %s' % HELP_TEXT, + ) + + @kb.add('s') + def _(event): + open_message( + 'Settings', + f'max_mb = {args.max_mb}\n' + f'kill_percent = {args.kill_percent}\n' + f'slice = {args.unit_name}\n' + f'swap_max_mb = {args.swap_max_mb}\n' + f'extra firefox args = {args.firefox_extra}', + ) + + @kb.add('a') + def _(event): + open_message('About', f'Version {__version__}\nCreated {__created__}') + + root = FloatContainer( + content=HSplit( + [ + Frame(body, title='Firefox Memory Manager'), + Window( + height=1, + content=FormattedTextControl( + HELP_TEXT, + ), + ), + ] + ), + floats=root_floats, + modal=True, + ) + + style = Style.from_dict( + { + 'frame.border': 'ansicyan', + 'dialog.body': 'bg:#444444', + 'dialog': 'bg:#888888', + } + ) + + app = Application( + layout=Layout(root), + key_bindings=gkb, + style=style, + full_screen=True, + refresh_interval=args.interval, + ) + + if args.firefox_cmd: + firefox_proc = launch_firefox_with_limits( + args.firefox_cmd, + memory_high=args.max_mb, + swap_max=args.swap_max_mb, # **fixed here** + extra_args=args.firefox_extra, + unit_name=args.unit_name, + ) + + def monitor_loop(): + nonlocal firefox_proc + while not terminate_flag.is_set(): + try: + refresh_body() + except: + logger.exception('') + + time.sleep(args.interval) + + # stop() + + terminate_flag = threading.Event() + t = threading.Thread(target=monitor_loop, daemon=True) + t.start() + + # refresh_body() + app.run( + # handle_sigint=True + ) # from prompt‑toolkit API :contentReference[oaicite:0]{index=0} + + t.join() + + stop() + + +if __name__ == '__main__': + main() diff --git a/python/online/fxreader/pr34/tasks/ble.py b/python/online/fxreader/pr34/tasks/ble.py index 48aa22c..b7ba7e5 100644 --- a/python/online/fxreader/pr34/tasks/ble.py +++ b/python/online/fxreader/pr34/tasks/ble.py @@ -23,7 +23,13 @@ async def f2(device, timeout=None): async def f3(client): - t1 = [dict(service=o.__dict__, characteristics=[o2.__dict__ for o2 in o.characteristics]) for o in client.services] + t1 = [ + dict( + service=o.__dict__, + characteristics=[o2.__dict__ for o2 in o.characteristics], + ) + for o in client.services + ] return t1 @@ -43,7 +49,13 @@ async def f5( t5 = {i: o.details[0].name() for i, o in enumerate(t1)} - t2.extend([t1[k] for k, v in t5.items() if isinstance(v, str) and name_check(v)]) + t2.extend( + [ + t1[k] + for k, v in t5.items() + if isinstance(v, str) and name_check(v) + ] + ) else: t2.extend(t1) @@ -66,7 +78,9 @@ async def f4( assert name_check in [ 'watch fit', ] - name_check2 = lambda current_name: name_check.lower() in current_name.lower() + name_check2 = ( + lambda current_name: name_check.lower() in current_name.lower() + ) else: name_check2 = name_check diff --git a/python/online/fxreader/pr34/tasks/cython.py b/python/online/fxreader/pr34/tasks/cython.py index a0b4f3f..58b63d2 100644 --- a/python/online/fxreader/pr34/tasks/cython.py +++ b/python/online/fxreader/pr34/tasks/cython.py @@ -66,7 +66,13 @@ def build(content: str, module: M) -> M: # ) t1.run() - return cast(M, Cython.Build.Inline.load_dynamic('_%s' % sha256sum, glob.glob(str(output_dir / ('_%s*.so' % sha256sum)))[0])) + return cast( + M, + Cython.Build.Inline.load_dynamic( + '_%s' % sha256sum, + glob.glob(str(output_dir / ('_%s*.so' % sha256sum)))[0], + ), + ) raise NotImplementedError @@ -125,7 +131,9 @@ def mypyc_build(file_path: pathlib.Path) -> Any: # f.write(content) t1 = Cython.Build.Inline._get_build_extension() - t1.extensions = mypyc.build.mypycify([str(source_path)], target_dir=str(output_dir / 'build')) + t1.extensions = mypyc.build.mypycify( + [str(source_path)], target_dir=str(output_dir / 'build') + ) t1.build_temp = str(output_dir) t1.build_lib = str(lib_dir) # t2 = Cython.Build.Inline.Extension( @@ -147,7 +155,11 @@ def mypyc_build(file_path: pathlib.Path) -> Any: class Source: @staticmethod - def test2(_a: numpy.ndarray[Any, numpy.dtype[numpy.int64]], _id: numpy.dtype[numpy.int32] | int, T: float = 16) -> int: + def test2( + _a: numpy.ndarray[Any, numpy.dtype[numpy.int64]], + _id: numpy.dtype[numpy.int32] | int, + T: float = 16, + ) -> int: raise NotImplementedError @@ -243,7 +255,11 @@ def test_cython(N: int = 4, T: int = 16) -> None: def test_mypyc(N: int = 4, W: int = 35) -> None: - cython2 = mypyc_build((pathlib.Path(__file__).parent / 'cython2.py').relative_to(pathlib.Path.cwd())) + cython2 = mypyc_build( + (pathlib.Path(__file__).parent / 'cython2.py').relative_to( + pathlib.Path.cwd() + ) + ) # from .cython2 import fib diff --git a/python/online/fxreader/pr34/tasks/jigsaw_toxic.py b/python/online/fxreader/pr34/tasks/jigsaw_toxic.py index 2daac22..ea16158 100644 --- a/python/online/fxreader/pr34/tasks/jigsaw_toxic.py +++ b/python/online/fxreader/pr34/tasks/jigsaw_toxic.py @@ -73,8 +73,21 @@ def kernel_2(): from keras.layers.embeddings import Embedding from keras.layers.normalization import BatchNormalization from keras.utils import np_utils - from sklearn import preprocessing, decomposition, model_selection, metrics, pipeline - from keras.layers import GlobalMaxPooling1D, Conv1D, MaxPooling1D, Flatten, Bidirectional, SpatialDropout1D + from sklearn import ( + preprocessing, + decomposition, + model_selection, + metrics, + pipeline, + ) + from keras.layers import ( + GlobalMaxPooling1D, + Conv1D, + MaxPooling1D, + Flatten, + Bidirectional, + SpatialDropout1D, + ) from keras.preprocessing import sequence, text from keras.callbacks import EarlyStopping @@ -112,15 +125,25 @@ def kernel_2(): print('REPLICAS: ', strategy.num_replicas_in_sync) # %% [code] - train = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/jigsaw-toxic-comment-train.csv') - validation = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/validation.csv') - test = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/test.csv') + train = pd.read_csv( + '/kaggle/input/jigsaw-multilingual-toxic-comment-classification/jigsaw-toxic-comment-train.csv' + ) + validation = pd.read_csv( + '/kaggle/input/jigsaw-multilingual-toxic-comment-classification/validation.csv' + ) + test = pd.read_csv( + '/kaggle/input/jigsaw-multilingual-toxic-comment-classification/test.csv' + ) # %% [markdown] # We will drop the other columns and approach this problem as a Binary Classification Problem and also we will have our exercise done on a smaller subsection of the dataset(only 12000 data points) to make it easier to train the models # %% [code] - train.drop(['severe_toxic', 'obscene', 'threat', 'insult', 'identity_hate'], axis=1, inplace=True) + train.drop( + ['severe_toxic', 'obscene', 'threat', 'insult', 'identity_hate'], + axis=1, + inplace=True, + ) # %% [code] train = train.loc[:12000, :] @@ -137,7 +160,12 @@ def kernel_2(): # %% [code] xtrain, xvalid, ytrain, yvalid = train_test_split( - train.comment_text.values, train.toxic.values, stratify=train.toxic.values, random_state=42, test_size=0.2, shuffle=True + train.comment_text.values, + train.toxic.values, + stratify=train.toxic.values, + random_state=42, + test_size=0.2, + shuffle=True, ) # %% [markdown] @@ -206,7 +234,9 @@ def kernel_2(): model.add(Embedding(len(word_index) + 1, 300, input_length=max_len)) model.add(SimpleRNN(100)) model.add(Dense(1, activation='sigmoid')) - model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) + model.compile( + loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'] + ) model.summary() @@ -253,7 +283,10 @@ def kernel_3( o_2['model'].load_weights('model.h5') else: o_2['model'].fit( - o_2['xtrain_pad'], o_2['ytrain'], nb_epoch=nb_epochs, batch_size=64 * o_2['strategy'].num_replicas_in_sync + o_2['xtrain_pad'], + o_2['ytrain'], + nb_epoch=nb_epochs, + batch_size=64 * o_2['strategy'].num_replicas_in_sync, ) # Multiplying by Strategy to run on TPU's o_2['model'].save_weights('model.h5') @@ -263,7 +296,9 @@ def kernel_3( # %% [code] scores_model = [] - scores_model.append({'Model': 'SimpleRNN', 'AUC_Score': roc_auc(scores, o_2['yvalid'])}) + scores_model.append( + {'Model': 'SimpleRNN', 'AUC_Score': roc_auc(scores, o_2['yvalid'])} + ) # %% [markdown] # ## Code Explanantion @@ -283,7 +318,12 @@ def kernel_4( import keras.preprocessing.sequence if input_texts is None: - input_texts = ['blahb blahb blah', 'Hello World!', 'This is very good!', 'A very non toxic comment! This is so polite and polished one!'] + input_texts = [ + 'blahb blahb blah', + 'Hello World!', + 'This is very good!', + 'A very non toxic comment! This is so polite and polished one!', + ] t6 = [] for o in input_texts: @@ -291,7 +331,9 @@ def kernel_4( t2 = o_2['token'].texts_to_sequences( [t1], ) - t3 = keras.preprocessing.sequence.pad_sequences(t2, maxlen=o_2['max_len']) + t3 = keras.preprocessing.sequence.pad_sequences( + t2, maxlen=o_2['max_len'] + ) t4 = o_2['model'].predict( t3, ) diff --git a/python/online/fxreader/pr34/tasks/mlb_player.py b/python/online/fxreader/pr34/tasks/mlb_player.py index 35701ef..a2d8fa3 100644 --- a/python/online/fxreader/pr34/tasks/mlb_player.py +++ b/python/online/fxreader/pr34/tasks/mlb_player.py @@ -42,12 +42,26 @@ def kernel_2( ): t1 = {} - for k in ['playerTwitterFollowers', 'teamTwitterFollowers', 'games', 'events']: + for k in [ + 'playerTwitterFollowers', + 'teamTwitterFollowers', + 'games', + 'events', + ]: t4 = '%s.nc' % k if not os.path.exists(t4): print('started %s' % t4) t2 = '/kaggle/input/mlb-player-digital-engagement-forecasting/train.csv' - t3 = pandas.DataFrame(sum([json.loads(o) for o in o_1['t3'][t2][k].values if isinstance(o, str)], [])).to_xarray() + t3 = pandas.DataFrame( + sum( + [ + json.loads(o) + for o in o_1['t3'][t2][k].values + if isinstance(o, str) + ], + [], + ) + ).to_xarray() t3.to_netcdf(t4) print('cached %s' % t4) @@ -55,7 +69,9 @@ def kernel_2( t5 = '%s-v2.nc' % k if not os.path.exists(t5): t2 = xarray.load_dataset(t4) - t3 = t2.sel(index=numpy.arange(2017653 - 10 * 1000, 2017653 + 1)) + t3 = t2.sel( + index=numpy.arange(2017653 - 10 * 1000, 2017653 + 1) + ) t3.to_netcdf(t5) t1[k] = xarray.load_dataset(t5) print('loaded %s' % t5) @@ -119,9 +135,15 @@ def kernel_3(should_exist=None): def kernel_4( o_3=None, ): - [print(o_3['t5']['events'].to_dataframe().iloc[k].to_json(indent=4)) for k in range(-10, -1)] + [ + print(o_3['t5']['events'].to_dataframe().iloc[k].to_json(indent=4)) + for k in range(-10, -1) + ] - [print(o_3['t5']['games'].to_dataframe().iloc[k].to_json(indent=4)) for k in range(-10, -1)] + [ + print(o_3['t5']['games'].to_dataframe().iloc[k].to_json(indent=4)) + for k in range(-10, -1) + ] t4 = 'https://www.youtube.com/watch?v=reaC7BHgL3M' @@ -264,7 +286,9 @@ def kernel_6( try: cap = cv2.VideoCapture(o) - fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS" + fps = cap.get( + cv2.CAP_PROP_FPS + ) # OpenCV2 version 2 used "CV_CAP_PROP_FPS" frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) duration = frame_count / fps finally: @@ -454,15 +478,31 @@ def kernel_7( for k in layer: v = layer[k] if 'pool' in k: - layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2])] + layers += [ + nn.MaxPool2d( + kernel_size=v[0], stride=v[1], padding=v[2] + ) + ] else: - conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4]) + conv2d = nn.Conv2d( + in_channels=v[0], + out_channels=v[1], + kernel_size=v[2], + stride=v[3], + padding=v[4], + ) layers += [conv2d, nn.ReLU(inplace=True)] layer = list(layer_dict[-1].keys()) k = layer[0] v = layer_dict[-1][k] - conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4]) + conv2d = nn.Conv2d( + in_channels=v[0], + out_channels=v[1], + kernel_size=v[2], + stride=v[3], + padding=v[4], + ) layers += [conv2d] return nn.Sequential(*layers) @@ -530,9 +570,19 @@ def kernel_7( for key in block: v = block[key] if 'pool' in key: - layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2])] + layers += [ + nn.MaxPool2d( + kernel_size=v[0], stride=v[1], padding=v[2] + ) + ] else: - conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4]) + conv2d = nn.Conv2d( + in_channels=v[0], + out_channels=v[1], + kernel_size=v[2], + stride=v[3], + padding=v[4], + ) layers += [conv2d, nn.ReLU(inplace=True)] models = {'block_0': nn.Sequential(*layers)} @@ -543,16 +593,38 @@ def kernel_7( return PoseEstimation(models) - def get_paf_and_heatmap(model, img_raw, scale_search, param_stride=8, box_size=368): - multiplier = [scale * box_size / img_raw.shape[0] for scale in scale_search] + def get_paf_and_heatmap( + model, img_raw, scale_search, param_stride=8, box_size=368 + ): + multiplier = [ + scale * box_size / img_raw.shape[0] for scale in scale_search + ] - heatmap_avg = torch.zeros((len(multiplier), 19, img_raw.shape[0], img_raw.shape[1])).cuda() - paf_avg = torch.zeros((len(multiplier), 38, img_raw.shape[0], img_raw.shape[1])).cuda() + heatmap_avg = torch.zeros( + (len(multiplier), 19, img_raw.shape[0], img_raw.shape[1]) + ).cuda() + paf_avg = torch.zeros( + (len(multiplier), 38, img_raw.shape[0], img_raw.shape[1]) + ).cuda() for i, scale in enumerate(multiplier): - img_test = cv2.resize(img_raw, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) - img_test_pad, pad = pad_right_down_corner(img_test, param_stride, param_stride) - img_test_pad = np.transpose(np.float32(img_test_pad[:, :, :, np.newaxis]), (3, 2, 0, 1)) / 256 - 0.5 + img_test = cv2.resize( + img_raw, + (0, 0), + fx=scale, + fy=scale, + interpolation=cv2.INTER_CUBIC, + ) + img_test_pad, pad = pad_right_down_corner( + img_test, param_stride, param_stride + ) + img_test_pad = ( + np.transpose( + np.float32(img_test_pad[:, :, :, np.newaxis]), (3, 2, 0, 1) + ) + / 256 + - 0.5 + ) feed = Variable(torch.from_numpy(img_test_pad)).cuda() output1, output2 = model(feed) @@ -560,17 +632,27 @@ def kernel_7( # print(output1.size()) # print(output2.size()) - heatmap = nn.UpsamplingBilinear2d((img_raw.shape[0], img_raw.shape[1])).cuda()(output2) + heatmap = nn.UpsamplingBilinear2d( + (img_raw.shape[0], img_raw.shape[1]) + ).cuda()(output2) - paf = nn.UpsamplingBilinear2d((img_raw.shape[0], img_raw.shape[1])).cuda()(output1) + paf = nn.UpsamplingBilinear2d( + (img_raw.shape[0], img_raw.shape[1]) + ).cuda()(output1) heatmap_avg[i] = heatmap[0].data paf_avg[i] = paf[0].data - heatmap_avg = torch.transpose(torch.transpose(torch.squeeze(torch.mean(heatmap_avg, 0)), 0, 1), 1, 2).cuda() + heatmap_avg = torch.transpose( + torch.transpose(torch.squeeze(torch.mean(heatmap_avg, 0)), 0, 1), + 1, + 2, + ).cuda() heatmap_avg = heatmap_avg.cpu().numpy() - paf_avg = torch.transpose(torch.transpose(torch.squeeze(torch.mean(paf_avg, 0)), 0, 1), 1, 2).cuda() + paf_avg = torch.transpose( + torch.transpose(torch.squeeze(torch.mean(paf_avg, 0)), 0, 1), 1, 2 + ).cuda() paf_avg = paf_avg.cpu().numpy() return paf_avg, heatmap_avg @@ -592,20 +674,34 @@ def kernel_7( map_down = np.zeros(map_gau.shape) map_down[:, :-1] = map_gau[:, 1:] - peaks_binary = np.logical_and.reduce((map_gau >= map_left, map_gau >= map_right, map_gau >= map_up, map_gau >= map_down, map_gau > param_thre1)) + peaks_binary = np.logical_and.reduce( + ( + map_gau >= map_left, + map_gau >= map_right, + map_gau >= map_up, + map_gau >= map_down, + map_gau > param_thre1, + ) + ) - peaks = zip(np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0]) # note reverse + peaks = zip( + np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0] + ) # note reverse peaks = list(peaks) peaks_with_score = [x + (map_ori[x[1], x[0]],) for x in peaks] ids = range(peak_counter, peak_counter + len(peaks)) - peaks_with_score_and_id = [peaks_with_score[i] + (ids[i],) for i in range(len(ids))] + peaks_with_score_and_id = [ + peaks_with_score[i] + (ids[i],) for i in range(len(ids)) + ] all_peaks.append(peaks_with_score_and_id) peak_counter += len(peaks) return all_peaks - def extract_paf_info(img_raw, paf_avg, all_peaks, param_thre2=0.05, param_thre3=0.5): + def extract_paf_info( + img_raw, paf_avg, all_peaks, param_thre2=0.05, param_thre3=0.5 + ): connection_all = [] special_k = [] mid_num = 10 @@ -626,27 +722,69 @@ def kernel_7( raise ZeroDivisionError vec = np.divide(vec, norm) - startend = zip(np.linspace(candA[i][0], candB[j][0], num=mid_num), np.linspace(candA[i][1], candB[j][1], num=mid_num)) + startend = zip( + np.linspace(candA[i][0], candB[j][0], num=mid_num), + np.linspace(candA[i][1], candB[j][1], num=mid_num), + ) startend = list(startend) - vec_x = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 0] for I in range(len(startend))]) - vec_y = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 1] for I in range(len(startend))]) + vec_x = np.array( + [ + score_mid[ + int(round(startend[I][1])), + int(round(startend[I][0])), + 0, + ] + for I in range(len(startend)) + ] + ) + vec_y = np.array( + [ + score_mid[ + int(round(startend[I][1])), + int(round(startend[I][0])), + 1, + ] + for I in range(len(startend)) + ] + ) - score_midpts = np.multiply(vec_x, vec[0]) + np.multiply(vec_y, vec[1]) - score_with_dist_prior = sum(score_midpts) / len(score_midpts) - score_with_dist_prior += min(0.5 * img_raw.shape[0] / norm - 1, 0) + score_midpts = np.multiply(vec_x, vec[0]) + np.multiply( + vec_y, vec[1] + ) + score_with_dist_prior = sum(score_midpts) / len( + score_midpts + ) + score_with_dist_prior += min( + 0.5 * img_raw.shape[0] / norm - 1, 0 + ) - criterion1 = len(np.nonzero(score_midpts > param_thre2)[0]) > 0.8 * len(score_midpts) + criterion1 = len( + np.nonzero(score_midpts > param_thre2)[0] + ) > 0.8 * len(score_midpts) criterion2 = score_with_dist_prior > 0 if criterion1 and criterion2: - connection_candidate.append([i, j, score_with_dist_prior, score_with_dist_prior + candA[i][2] + candB[j][2]]) + connection_candidate.append( + [ + i, + j, + score_with_dist_prior, + score_with_dist_prior + + candA[i][2] + + candB[j][2], + ] + ) - connection_candidate = sorted(connection_candidate, key=lambda x: x[2], reverse=True) + connection_candidate = sorted( + connection_candidate, key=lambda x: x[2], reverse=True + ) connection = np.zeros((0, 5)) for c in range(len(connection_candidate)): i, j, s = connection_candidate[c][0:3] if i not in connection[:, 3] and j not in connection[:, 4]: - connection = np.vstack([connection, [candA[i][3], candB[j][3], s, i, j]]) + connection = np.vstack( + [connection, [candA[i][3], candB[j][3], s, i, j]] + ) if len(connection) >= min(nA, nB): break @@ -661,7 +799,9 @@ def kernel_7( # last number in each row is the total parts number of that person # the second last number in each row is the score of the overall configuration subset = -1 * np.ones((0, 20)) - candidate = np.array([item for sublist in all_peaks for item in sublist]) + candidate = np.array( + [item for sublist in all_peaks for item in sublist] + ) for k in range(len(map_ids)): if k not in special_k: @@ -673,7 +813,10 @@ def kernel_7( found = 0 subset_idx = [-1, -1] for j in range(len(subset)): # 1:size(subset,1): - if subset[j][indexA] == partAs[i] or subset[j][indexB] == partBs[i]: + if ( + subset[j][indexA] == partAs[i] + or subset[j][indexB] == partBs[i] + ): subset_idx[found] = j found += 1 @@ -682,11 +825,17 @@ def kernel_7( if subset[j][indexB] != partBs[i]: subset[j][indexB] = partBs[i] subset[j][-1] += 1 - subset[j][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] + subset[j][-2] += ( + candidate[partBs[i].astype(int), 2] + + connection_all[k][i][2] + ) elif found == 2: # if found 2 and disjoint, merge them j1, j2 = subset_idx print('found = 2') - membership = ((subset[j1] >= 0).astype(int) + (subset[j2] >= 0).astype(int))[:-2] + membership = ( + (subset[j1] >= 0).astype(int) + + (subset[j2] >= 0).astype(int) + )[:-2] if len(np.nonzero(membership == 2)[0]) == 0: # merge subset[j1][:-2] += subset[j2][:-2] + 1 subset[j1][-2:] += subset[j2][-2:] @@ -695,7 +844,10 @@ def kernel_7( else: # as like found == 1 subset[j1][indexB] = partBs[i] subset[j1][-1] += 1 - subset[j1][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] + subset[j1][-2] += ( + candidate[partBs[i].astype(int), 2] + + connection_all[k][i][2] + ) # if find no partA in the subset, create a new subset elif not found and k < 17: @@ -703,7 +855,14 @@ def kernel_7( row[indexA] = partAs[i] row[indexB] = partBs[i] row[-1] = 2 - row[-2] = sum(candidate[connection_all[k][i, :2].astype(int), 2]) + connection_all[k][i][2] + row[-2] = ( + sum( + candidate[ + connection_all[k][i, :2].astype(int), 2 + ] + ) + + connection_all[k][i][2] + ) subset = np.vstack([subset, row]) return subset, candidate @@ -718,7 +877,9 @@ def kernel_7( for i in range(18): for j in range(len(all_peaks[i])): - cv2.circle(img_canvas, all_peaks[i][j][0:2], 4, colors[i], thickness=-1) + cv2.circle( + img_canvas, all_peaks[i][j][0:2], 4, colors[i], thickness=-1 + ) return subset, img_canvas @@ -735,9 +896,18 @@ def kernel_7( mY = np.mean(Y) length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5 angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1])) - polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1) + polygon = cv2.ellipse2Poly( + (int(mY), int(mX)), + (int(length / 2), stickwidth), + int(angle), + 0, + 360, + 1, + ) cv2.fillConvexPoly(cur_canvas, polygon, colors[i]) - img_canvas = cv2.addWeighted(img_canvas, 0.4, cur_canvas, 0.6, 0) + img_canvas = cv2.addWeighted( + img_canvas, 0.4, cur_canvas, 0.6, 0 + ) return img_canvas @@ -754,11 +924,17 @@ def kernel_7( img_padded = img pad_up = np.tile(img_padded[0:1, :, :] * 0 + pad_value, (pad[0], 1, 1)) img_padded = np.concatenate((pad_up, img_padded), axis=0) - pad_left = np.tile(img_padded[:, 0:1, :] * 0 + pad_value, (1, pad[1], 1)) + pad_left = np.tile( + img_padded[:, 0:1, :] * 0 + pad_value, (1, pad[1], 1) + ) img_padded = np.concatenate((pad_left, img_padded), axis=1) - pad_down = np.tile(img_padded[-2:-1, :, :] * 0 + pad_value, (pad[2], 1, 1)) + pad_down = np.tile( + img_padded[-2:-1, :, :] * 0 + pad_value, (pad[2], 1, 1) + ) img_padded = np.concatenate((img_padded, pad_down), axis=0) - pad_right = np.tile(img_padded[:, -2:-1, :] * 0 + pad_value, (1, pad[3], 1)) + pad_right = np.tile( + img_padded[:, -2:-1, :] * 0 + pad_value, (1, pad[3], 1) + ) img_padded = np.concatenate((img_padded, pad_right), axis=1) return img_padded, pad @@ -784,11 +960,15 @@ def kernel_7( # In[4]: - state_dict = torch.load(model)['state_dict'] # getting the pre-trained model's parameters + state_dict = torch.load(model)[ + 'state_dict' + ] # getting the pre-trained model's parameters # A state_dict is simply a Python dictionary object that maps each layer to its parameter tensor. model_pose = get_pose_model() # building the model (see fn. defn. above). To see the architecture, see below cell. - model_pose.load_state_dict(state_dict) # Loading the parameters (weights, biases) into the model. + model_pose.load_state_dict( + state_dict + ) # Loading the parameters (weights, biases) into the model. model_pose.float() # I'm not sure why this is used. No difference if you remove it. @@ -797,7 +977,9 @@ def kernel_7( if use_gpu: model_pose.cuda() - model_pose = torch.nn.DataParallel(model_pose, device_ids=range(torch.cuda.device_count())) + model_pose = torch.nn.DataParallel( + model_pose, device_ids=range(torch.cuda.device_count()) + ) cudnn.benchmark = True def estimate_pose( @@ -833,7 +1015,9 @@ def kernel_7( img_points = None try: - paf_info, heatmap_info = get_paf_and_heatmap(model_pose, img_ori, scale_param) + paf_info, heatmap_info = get_paf_and_heatmap( + model_pose, img_ori, scale_param + ) peaks = extract_heatmap_info(heatmap_info) sp_k, con_all = extract_paf_info(img_ori, paf_info, peaks) @@ -876,7 +1060,13 @@ def kernel_7( def kernel_8( o_7, ): - for i, o in enumerate(['../input/indonesian-traditional-dance/tgagrakanyar/tga_00%d0.jpg' % k for k in range(6)]): + for i, o in enumerate( + [ + '../input/indonesian-traditional-dance/tgagrakanyar/tga_00%d0.jpg' + % k + for k in range(6) + ] + ): arch_image = o img_ori = o_7['cv2'].imread(arch_image) o_7['estimate_pose'](img_ori) @@ -887,7 +1077,9 @@ def kernel_9_benchmark( ): import datetime - t1 = o_7['cv2'].imread('../input/indonesian-traditional-dance/tgagrakanyar/tga_0000.jpg') + t1 = o_7['cv2'].imread( + '../input/indonesian-traditional-dance/tgagrakanyar/tga_0000.jpg' + ) t5 = 10 t2 = datetime.datetime.now() for k in range(t5): @@ -905,7 +1097,9 @@ def kernel_10(): import torch # Model - model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # or yolov5m, yolov5x, custom + model = torch.hub.load( + 'ultralytics/yolov5', 'yolov5s' + ) # or yolov5m, yolov5x, custom # Images img = 'https://ultralytics.com/images/zidane.jpg' # or file, PIL, OpenCV, numpy, multiple @@ -927,7 +1121,9 @@ def kernel_11_benchmark( ): import datetime - t1 = o_7['cv2'].imread('../input/indonesian-traditional-dance/tgagrakanyar/tga_0000.jpg') + t1 = o_7['cv2'].imread( + '../input/indonesian-traditional-dance/tgagrakanyar/tga_0000.jpg' + ) t5 = 10 t2 = datetime.datetime.now() for k in range(t5): @@ -956,7 +1152,18 @@ def kernel_13( if not len(t4) > 0 or not o_6 is None: t1 = pandas.concat( - sum([[o2['t11'][0].assign(frame_id=k, video_path=o['video_path']) for k, o2 in enumerate(o['frames'])] for o in o_6['t8']], []) + sum( + [ + [ + o2['t11'][0].assign( + frame_id=k, video_path=o['video_path'] + ) + for k, o2 in enumerate(o['frames']) + ] + for o in o_6['t8'] + ], + [], + ) ).to_xarray() t5 = t3[0] t1.to_netcdf(t5) @@ -1028,7 +1235,9 @@ def kernel_14( def kernel_15( o_14, ): - t1 = pandas.DataFrame(numpy.unique(o_14['o_13']['t1']['name'].data, return_counts=True)).T + t1 = pandas.DataFrame( + numpy.unique(o_14['o_13']['t1']['name'].data, return_counts=True) + ).T pprint.pprint( dict( t1=t1, @@ -1078,7 +1287,9 @@ def kernel_15( t12 = cv2.cvtColor(t11, cv2.COLOR_BGR2RGB) t13 = t12.copy() t15 = numpy.array([t8.xcenter, t8.ycenter, t8.width, t8.height]) - t16 = numpy.array([t13.shape[1], t13.shape[0], t13.shape[1], t13.shape[0]]) + t16 = numpy.array( + [t13.shape[1], t13.shape[0], t13.shape[1], t13.shape[0]] + ) t17 = t15 * t16 t18 = t17[:2] - t17[2:] / 2 t19 = t17[:2] + t17[2:] / 2 @@ -1340,7 +1551,10 @@ def kernel_20( t1 = numpy.array(o_18['t2']['t7'][0]['keypoints']).reshape(17, -1) t2 = o_18['t2']['t6'][0] t3 = o_18['t2']['t1'][0]['image_canvas'].copy() - assert o_18['t2']['t7'][0]['image_id'] == os.path.split(o_18['t2']['t1'][0]['image_name'])[1] + assert ( + o_18['t2']['t7'][0]['image_id'] + == os.path.split(o_18['t2']['t1'][0]['image_name'])[1] + ) for i, o2 in enumerate(o_21['p_color']): if i >= 17: @@ -1449,7 +1663,16 @@ def kernel_22(o_18): o_31 = kernel_31( image_id=[o['image_id'] for o in t1], - image_size=numpy.array([[list(o['image_canvas'].shape) for o in o_18['t2']['t1'] if o['image_name'] == t1[i]['image_id']][0] for i in range(len(t2))]), + image_size=numpy.array( + [ + [ + list(o['image_canvas'].shape) + for o in o_18['t2']['t1'] + if o['image_name'] == t1[i]['image_id'] + ][0] + for i in range(len(t2)) + ] + ), keypoints=numpy.stack(t2, axis=0), ) t12 = o_31['t12'] @@ -1558,7 +1781,11 @@ def kernel_25(images, delay=None): def kernel_26(o_18, image_name): - t1 = [i for i, o in enumerate(o_18['t2']['t1']) if o['image_name'] == image_name] + t1 = [ + i + for i, o in enumerate(o_18['t2']['t1']) + if o['image_name'] == image_name + ] assert len(t1) == 1 return t1[0] @@ -1580,7 +1807,11 @@ def kernel_23(o_18, o_22, ids=None): t9 = kernel_26(o_18=o_18, image_name=t3['image_name']) t4 = o_18['t2']['t1'][t9]['image_canvas'] t10 = o_18['t2']['t6'][t9] - t4 = [o['image_canvas'] for o in o_18['t2']['t1'] if o['image_name'] == t3['image_name']] + t4 = [ + o['image_canvas'] + for o in o_18['t2']['t1'] + if o['image_name'] == t3['image_name'] + ] assert len(t4) == 1 t5 = t4[0] t6 = kernel_24(t5, t3['keypoints']) @@ -1641,7 +1872,9 @@ def kernel_27(): """ % (t4, t2) if False: pprint.pprint([t4, t2, t6]) - with subprocess.Popen(t6, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + with subprocess.Popen( + t6, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) as p: if False: pprint.pprint(p.communicate()) p.wait() @@ -1669,7 +1902,9 @@ def kernel_28( max_seconds = 999999 if video_path is None: - video_path = '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + video_path = ( + '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + ) t5 = video_path t3 = '/kaggle/working/kernel_28-output%s.dir' % video_id t13 = '/root/kernel_28-output.dir/tmp-slice' @@ -1679,7 +1914,9 @@ def kernel_28( try: cap = cv2.VideoCapture(t5) - fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS" + fps = cap.get( + cv2.CAP_PROP_FPS + ) # OpenCV2 version 2 used "CV_CAP_PROP_FPS" frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) real_duration = frame_count / fps duration = min(real_duration, max_seconds) @@ -1739,7 +1976,9 @@ def kernel_28( t6, ] ) - with subprocess.Popen(t6, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + with subprocess.Popen( + t6, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) as p: if False: pprint.pprint(p.communicate()) p.wait() @@ -1757,7 +1996,9 @@ def kernel_29( video_id = '' if video_path is None: - video_path = '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + video_path = ( + '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + ) assert os.path.exists(video_path) @@ -1771,7 +2012,13 @@ def kernel_29( t7 = [o for o in t6 if os.path.exists(o)] if len(t7) == 0: - t1 = [dict(data=json.load(io.open(o, 'r')), input_path=o) for o in glob.glob('/kaggle/working/kernel_28-output%s.dir/slice-*/*.json' % video_id)] + t1 = [ + dict(data=json.load(io.open(o, 'r')), input_path=o) + for o in glob.glob( + '/kaggle/working/kernel_28-output%s.dir/slice-*/*.json' + % video_id + ) + ] assert len(t1) > 0 @@ -1835,7 +2082,9 @@ def kernel_30( low_mean_conf = 0.6 if video_path is None: - video_path = '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + video_path = ( + '/kaggle/working/ATL AT TOR - April 19, 2015-T0MUK91ZWys.mp4' + ) if max_frames is None: max_frames = 9999 @@ -2045,7 +2294,10 @@ def kernel_31(image_id, image_size, keypoints): ab = [a[0] - b[0], a[1] - b[1]] ab1 = [c[0] - d[0], c[1] - d[1]] - cos = abs(ab[0] * ab1[0] + ab[1] * ab1[1]) / (sqrt(ab[0] ** 2 + ab[1] ** 2) * sqrt(ab1[0] ** 2 + ab1[1] ** 2) + 1e-8) + cos = abs(ab[0] * ab1[0] + ab[1] * ab1[1]) / ( + sqrt(ab[0] ** 2 + ab[1] ** 2) * sqrt(ab1[0] ** 2 + ab1[1] ** 2) + + 1e-8 + ) ang = acos(cos) return ang * 180 / np.pi @@ -2204,7 +2456,11 @@ def kernel_33(): o_22 = kernel_22(o_18=o_18) import pandas - o_23 = kernel_23(o_18=o_18, o_22=o_22, ids=pandas.DataFrame(o_22['t4']).query('portion > 0.1').index.values) + o_23 = kernel_23( + o_18=o_18, + o_22=o_22, + ids=pandas.DataFrame(o_22['t4']).query('portion > 0.1').index.values, + ) o_27 = kernel_27() o_28 = kernel_28() o_29 = kernel_29() @@ -2273,7 +2529,9 @@ def kernel_36(): # import os from os.path import exists, join, basename, splitext - git_repo_url = 'https://github.com/CMU-Perceptual-Computing-Lab/openpose.git' + git_repo_url = ( + 'https://github.com/CMU-Perceptual-Computing-Lab/openpose.git' + ) project_name = splitext(basename(git_repo_url))[0] if 1 or not exists(project_name): @@ -2282,8 +2540,18 @@ def kernel_36(): print('install new CMake becaue of CUDA10') cmake_version = 'cmake-3.20.2-linux-x86_64.tar.gz' if not exists(cmake_version): - assert os.system(r"""!wget -q 'https://cmake.org/files/v3.20/{cmake_version}' """) == 0 - assert os.system(r"""!tar xfz {cmake_version} --strip-components=1 -C /usr/local """) == 0 + assert ( + os.system( + r"""!wget -q 'https://cmake.org/files/v3.20/{cmake_version}' """ + ) + == 0 + ) + assert ( + os.system( + r"""!tar xfz {cmake_version} --strip-components=1 -C /usr/local """ + ) + == 0 + ) print('clone openpose') assert os.system(r"""!git clone -q --depth 1 $git_repo_url """) == 0 @@ -2295,7 +2563,12 @@ def kernel_36(): == 0 ) print('build openpose') - assert os.system(r"""!cd openpose && rm -rf build || true && mkdir build && cd build && cmake .. && make -j`nproc` """) == 0 + assert ( + os.system( + r"""!cd openpose && rm -rf build || true && mkdir build && cd build && cmake .. && make -j`nproc` """ + ) + == 0 + ) """## From a Google Drive's folder""" @@ -2310,7 +2583,9 @@ def kernel_36(): print(filename) colab_video_path = folder_path + filename print(colab_video_path) - colab_openpose_video_path = colab_video_path.replace('.mp4', '') + '-openpose.mp4' + colab_openpose_video_path = ( + colab_video_path.replace('.mp4', '') + '-openpose.mp4' + ) print(colab_openpose_video_path) if not exists(colab_openpose_video_path): assert ( @@ -2325,9 +2600,16 @@ def kernel_36(): assert os.system(r"""!pip install youtube-dl """) == 0 youtube_id = '2021-05-07_22-00-55_UTC' - assert os.system(r"""!youtube-dl -f mp4 -o '/content/drive/My Drive/openpose/%(id)s.mp4' {youtube_id} """) == 0 + assert ( + os.system( + r"""!youtube-dl -f mp4 -o '/content/drive/My Drive/openpose/%(id)s.mp4' {youtube_id} """ + ) + == 0 + ) colab_video_path = '/content/drive/My Drive/openpose/' + youtube_id + '.mp4' - colab_openpose_video_path = colab_video_path.replace('.mp4', '') + '-openpose.mp4' + colab_openpose_video_path = ( + colab_video_path.replace('.mp4', '') + '-openpose.mp4' + ) assert ( os.system( @@ -2352,7 +2634,9 @@ def kernel_36(): # from os.path import exists, join, basename, splitext # colab_video_path = '/content/drive/My Drive/bachata.mp4' colab_video_path = '/content/output.mp4' - colab_openpose_video_path = colab_video_path.replace('.mp4', '') + '-openpose.mp4' + colab_openpose_video_path = ( + colab_video_path.replace('.mp4', '') + '-openpose.mp4' + ) assert ( os.system( diff --git a/python/pyproject.common.toml b/python/pyproject.common.toml index 6bf90fd..035c6b9 100644 --- a/python/pyproject.common.toml +++ b/python/pyproject.common.toml @@ -21,7 +21,8 @@ dependencies = [ 'pydantic', 'pydantic-settings', 'tomlkit', - 'pip==23.3.2', + 'tomlq', + 'pip==25.1', ] [project.optional-dependencies] @@ -29,6 +30,16 @@ crypto = [ 'cryptography', ] +django = [ + 'django', +] + +fastapi = [ + 'uvicorn', + 'fastapi', + 'uvloop', +] + early = [ 'numpy', 'cryptography', @@ -36,6 +47,7 @@ early = [ 'toml-cli', 'ninja', 'patchelf', + 'online.fxreader.pr34', # 'tomlkit', ] @@ -47,17 +59,26 @@ lint = [ 'tomli', # 'tomllib', 'mypy', + 'django-stubs', 'pyright', 'pyrefly', 'ruff', + 'fastapi', + 'uvicorn', + 'pip==25.1', # 'tomlkit', ] [project.scripts] online-fxreader-pr34-commands = 'online.fxreader.pr34.commands:commands_cli' +oom_firefox = 'online.fxreader.pr34.oom_firefox:main' [tool.online-fxreader-pr34] early_features = ['default', 'early', 'lint'] +pip_find_links = [ + 'deps/whl', +] +requirements = { default_early_lint_3_13 = 'requirements.3.13.txt' } modules = [ { name = 'online.fxreader.pr34', tool = { 'online-fxreader-pr34' = { early_features = ['default', 'early', 'lint'] } } }, diff --git a/python/requirements.3.13.txt b/python/requirements.3.13.txt new file mode 100644 index 0000000..cae6bfc --- /dev/null +++ b/python/requirements.3.13.txt @@ -0,0 +1,126 @@ +annotated-doc==0.0.4 \ + --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 +annotated-types==0.7.0 \ + --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 +anyio==4.10.0 \ + --hash=sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1 +argcomplete==3.6.2 \ + --hash=sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591 +asgiref==3.9.1 \ + --hash=sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c +build==1.3.0 \ + --hash=sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4 +cffi==1.17.1 \ + --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd +click==8.2.1 \ + --hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b +cryptography==45.0.6 \ + --hash=sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42 +django==5.2.5 \ + --hash=sha256:2b2ada0ee8a5ff743a40e2b9820d1f8e24c11bac9ae6469cd548f0057ea6ddcd +django-stubs==5.2.2 \ + --hash=sha256:79bd0fdbc78958a8f63e0b062bd9d03f1de539664476c0be62ade5f063c9e41e +django-stubs-ext==5.2.2 \ + --hash=sha256:8833bbe32405a2a0ce168d3f75a87168f61bd16939caf0e8bf173bccbd8a44c5 +fastapi==0.116.1 \ + --hash=sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565 +h11==0.16.0 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 +idna==3.10 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 +jmespath==1.1.0 \ + --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 +marisa-trie==1.3.1 \ + --hash=sha256:ecdb19d33b26738a32602ef432b06cc6deeca4b498ce67ba8e5e39c8a7c19745 +markdown-it-py==4.0.0 \ + --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 +meson==1.9.1 \ + --hash=sha256:f824ab770c041a202f532f69e114c971918ed2daff7ea56583d80642564598d0 +meson-python==0.18.0 \ + --hash=sha256:3b0fe051551cc238f5febb873247c0949cd60ded556efa130aa57021804868e2 +mypy==1.18.2 \ + --hash=sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d +mypy-extensions==1.1.0 \ + --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 +ninja==1.13.0 \ + --hash=sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa +nodeenv==1.9.1 \ + --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 +numpy==2.3.2 \ + --hash=sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f +online-fxreader-pr34==0.1.5.39 \ + --hash=sha256:b73f4caecb1ce6b94b4b3a87299e779b843ceb8d7a581da8d27abe622a6a37db +packaging==25.0 \ + --hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 +patchelf==0.17.2.4 \ + --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 +pip==25.1 \ + --hash=sha256:13b4aa0aaad055020a11bec8a1c2a70a2b2d080e12d89b962266029fff0a16ba +pybind11==3.0.1 \ + --hash=sha256:aa8f0aa6e0a94d3b64adfc38f560f33f15e589be2175e103c0a33c6bce55ee89 +pycparser==2.22 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc +pydantic==2.12.3 \ + --hash=sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf +pydantic-core==2.41.4 \ + --hash=sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84 +pydantic-settings==2.11.0 \ + --hash=sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c +pygments==2.20.0 \ + --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 +pyproject-hooks==1.2.0 \ + --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 +pyproject-metadata==0.9.1 \ + --hash=sha256:ee5efde548c3ed9b75a354fc319d5afd25e9585fa918a34f62f904cc731973ad +pyrefly==0.59.1 \ + --hash=sha256:59a2d01723b84d042f4fa6ec871ffd52d0a7e83b0ea791c2e0bb0ff750abce56 +pyright==1.1.407 \ + --hash=sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21 +python-dotenv==1.2.1 \ + --hash=sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61 +pyyaml==6.0.2 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 +regex==2026.4.4 \ + --hash=sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e +rich==14.3.3 \ + --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d +ruff==0.14.3 \ + --hash=sha256:0e2f8a0bbcffcfd895df39c9a4ecd59bb80dca03dc43f7fb63e647ed176b741e +setuptools==80.9.0 \ + --hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 +shellingham==1.5.4 \ + --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 +sqlparse==0.5.3 \ + --hash=sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca +starlette==0.47.3 \ + --hash=sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51 +toml-cli==0.8.2 \ + --hash=sha256:7af4679ca04c53ad0f6d300dab26f45a78fedf88e8310305bfe0a8ead37fd000 +tomli==2.3.0 \ + --hash=sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf +tomlkit==0.13.3 \ + --hash=sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0 +tomlq==0.1.0 \ + --hash=sha256:4b966fd999ed2bf69081b7c7f5caadbc4c9542d0ed5fcf2e9b7b4d8d7ada3c82 +typer==0.24.1 \ + --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e +types-pyyaml==6.0.12.20250822 \ + --hash=sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098 +typing-extensions==4.15.0 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 +typing-inspection==0.4.2 \ + --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 +uv==0.9.7 \ + --hash=sha256:8cf6bc2482d1293cc630f66b862b494c09acda9b7faff7307ef52667a2b3ad49 +uvicorn==0.35.0 \ + --hash=sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a +xmltodict==0.14.2 \ + --hash=sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac +yq==3.4.3 \ + --hash=sha256:547e34bc3caacce83665fd3429bf7c85f8e8b6b9aaee3f953db1ad716ff3434d diff --git a/python/requirements.txt b/python/requirements.txt deleted file mode 100644 index 89dfe92..0000000 --- a/python/requirements.txt +++ /dev/null @@ -1,1025 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile --generate-hashes -o /app/python/requirements.txt /tmp/requirementsqzkaj3cw.in -annotated-doc==0.0.4 \ - --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \ - --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4 - # via typer -annotated-types==0.7.0 \ - --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ - --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 - # via pydantic -argcomplete==3.6.3 \ - --hash=sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c \ - --hash=sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce - # via yq -build==1.4.0 \ - --hash=sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596 \ - --hash=sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936 - # via -r /tmp/requirementsqzkaj3cw.in -cffi==2.0.0 \ - --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \ - --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \ - --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ - --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ - --hash=sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44 \ - --hash=sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2 \ - --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ - --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \ - --hash=sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65 \ - --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \ - --hash=sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a \ - --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ - --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ - --hash=sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a \ - --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \ - --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ - --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \ - --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ - --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \ - --hash=sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c \ - --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ - --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \ - --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \ - --hash=sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb \ - --hash=sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165 \ - --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ - --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \ - --hash=sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c \ - --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \ - --hash=sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c \ - --hash=sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0 \ - --hash=sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743 \ - --hash=sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63 \ - --hash=sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5 \ - --hash=sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5 \ - --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ - --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \ - --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \ - --hash=sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93 \ - --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ - --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \ - --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ - --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ - --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ - --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \ - --hash=sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26 \ - --hash=sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322 \ - --hash=sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb \ - --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \ - --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ - --hash=sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4 \ - --hash=sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414 \ - --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ - --hash=sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664 \ - --hash=sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9 \ - --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ - --hash=sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739 \ - --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ - --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \ - --hash=sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe \ - --hash=sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9 \ - --hash=sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92 \ - --hash=sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5 \ - --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ - --hash=sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d \ - --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \ - --hash=sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f \ - --hash=sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495 \ - --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \ - --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ - --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \ - --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ - --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \ - --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \ - --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ - --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \ - --hash=sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7 \ - --hash=sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5 \ - --hash=sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534 \ - --hash=sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49 \ - --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \ - --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 \ - --hash=sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453 \ - --hash=sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf - # via cryptography -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 - # via typer -cryptography==46.0.5 \ - --hash=sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72 \ - --hash=sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235 \ - --hash=sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9 \ - --hash=sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356 \ - --hash=sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257 \ - --hash=sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad \ - --hash=sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4 \ - --hash=sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c \ - --hash=sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614 \ - --hash=sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed \ - --hash=sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31 \ - --hash=sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229 \ - --hash=sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0 \ - --hash=sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731 \ - --hash=sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b \ - --hash=sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4 \ - --hash=sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4 \ - --hash=sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263 \ - --hash=sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595 \ - --hash=sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1 \ - --hash=sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678 \ - --hash=sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48 \ - --hash=sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76 \ - --hash=sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0 \ - --hash=sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18 \ - --hash=sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d \ - --hash=sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d \ - --hash=sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1 \ - --hash=sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981 \ - --hash=sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7 \ - --hash=sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82 \ - --hash=sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2 \ - --hash=sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4 \ - --hash=sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663 \ - --hash=sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c \ - --hash=sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d \ - --hash=sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a \ - --hash=sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a \ - --hash=sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d \ - --hash=sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b \ - --hash=sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a \ - --hash=sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826 \ - --hash=sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee \ - --hash=sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9 \ - --hash=sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648 \ - --hash=sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da \ - --hash=sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2 \ - --hash=sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2 \ - --hash=sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87 - # via -r /tmp/requirementsqzkaj3cw.in -jmespath==1.1.0 \ - --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \ - --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 - # via toml-cli -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 - # via mypy -marisa-trie==1.4.0 \ - --hash=sha256:0107d847e1a706f50ba2f61c3d22a8d17d3d7581418a8e56217d5a02ce661cf9 \ - --hash=sha256:02f087ff7d9a48ff03f7b05469e2311fcdfc324c70e7e06789c311d7986de56b \ - --hash=sha256:04e25e6b5b96d16711b94f77b5903368034c9dff93217e246e7d35ddf1f3b922 \ - --hash=sha256:0516c3aa096e687cf4cd073be5d4c82a82a3e1e1912fb3140f67c556ac6df814 \ - --hash=sha256:065bb04bb08ad2be4131ea5219c7bf9d4b5eba11405eb05dc3251a654bf1a2e2 \ - --hash=sha256:06c5f4adbeb0bcaa6f451862b1aef8babe1b997d1e94281b69ce7c14bc369ebd \ - --hash=sha256:095d49ff69b139bdd24f45d437c28545df2764d29d2d921fae55a2a224272dd0 \ - --hash=sha256:13cd029f783ded1a1387ecdedbb893d2419cf074ff84ca26698ca9115a12471c \ - --hash=sha256:184742d267e56549c08d54083f874a5b8756287c4528b17e376058797a459b66 \ - --hash=sha256:1b570d01a5520d15224fa83651b9b9d200981c40c1114e815fb3452d9e2562cd \ - --hash=sha256:1ce444c5823dd1c6ac3db0f49acc78bc865e33d845a40688deea1cc7e663bcc7 \ - --hash=sha256:20bc1a6936ba13b6b69684df2e41592f39f700f478e62453bb07fcfbefe1aa30 \ - --hash=sha256:20dd1a9035c5cb225c6eeb8e516ebced9497bf0b48d60e9b556f38d8cdf76df8 \ - --hash=sha256:237713bb512122a28165c2a3eef227f98bffb0ce0c4baecf507a47e96a84f3d2 \ - --hash=sha256:23847dddec4151ed0da6e064087c2ff744d5110548781373e64fa01bb9c1a074 \ - --hash=sha256:26a4ba5f249bfa5a403c82149d4458804ee7f2017dfdc329164199a6f4434d7a \ - --hash=sha256:29cff29bf7bd41c8761d9fee0b972c784ae167d4c8bc85564b7cb607efe2b21a \ - --hash=sha256:3061214ceb9fa9dda7b2ca23be3b51acf4ab872670d2b4b515906f57079b603b \ - --hash=sha256:33cca20e60a78da01650d67ef97d60f3ec8a2b60d4dea2d65306fb418d17039d \ - --hash=sha256:36e3bf3b68862d2a229c0a5efc5b7879924cdb42e035d1aa4bcc9b96ce129e38 \ - --hash=sha256:4309258b8491f23e966cc330cfb07469cf1943dbc067cbac0ae656a16ce448f3 \ - --hash=sha256:45e31d2d4c7fa9492a3828c9620544edf714f11a86cfede85fff61c5de2a5ec1 \ - --hash=sha256:460df94a079d46e2c5e33a8cdf687d801bce67ec019765bda440f6800e2a69b5 \ - --hash=sha256:47435b3348766e1adc431d273cf569d2236a9e044c429207b11c3e2cf31abb30 \ - --hash=sha256:49559095f152ee1184a6b322a30818a89b02d60364932d606d330442b3935fc9 \ - --hash=sha256:4e637b999fb0681e8eb5d031297e38c265d2a93a556d20c9e2b885d8730e3baf \ - --hash=sha256:50f20203e1738928556a1c7193cdf7863787f5739d7af34aa5c10a7e64fa4062 \ - --hash=sha256:5b0f6af2195fbc11a7fd9798236f41722239adeaa79ea5e27a51a9a27951ac75 \ - --hash=sha256:5db435f7f5018f225e875ce00997bd341480b57c53634b9060ab29cae32cf9a5 \ - --hash=sha256:623c5c4eb5c63f117d877b76b3a6a239fde0aeed91eccb17422a2de741f614a0 \ - --hash=sha256:6b167d612804308d52bf00743480e17c67b5145bbecf3487def30f46295abd53 \ - --hash=sha256:70a4d63dbe2e73df72fba3bc12b4054eec4b9329430ac7209f8aa855163dcdf4 \ - --hash=sha256:73e61bee76e9e329a45a95b5fa5d50ecd06d9186d628b5e5a6b15c8de73b0789 \ - --hash=sha256:74b915983a5d46fd4a3b0ad55b8b5f7f2eed7aa2c24388f8298c83afeacb5bd4 \ - --hash=sha256:7b438b9fe4d599bb4c199aa973c91221579d167655df2f90f4da8fe3e67e6757 \ - --hash=sha256:85a776df7b369402d1b5f76af0ed4bd66f798c22fe0de90225c179dac40530a8 \ - --hash=sha256:89725a46007f4355d5a0ca7d75663433b416b2ff2f1099908042259f3eb4b6ad \ - --hash=sha256:908fe43cf371a40168efe8809c831ac6a8622cb6d050e656fb75caab1b424542 \ - --hash=sha256:9866f6dcff7e5ba714b1ca21aba6da881efab5b2bec656de5dc67d05cab300b6 \ - --hash=sha256:9a7c27b7ea89a49003da477fc07e6ca8ab8da79cd005c177bf9b92daad0244de \ - --hash=sha256:af79b6a73f1cd7034eb08081791bbaf9d264b8dac0a5a03933cec46e1316934b \ - --hash=sha256:b115df26be8baabdedb755e422aa7c4a0bfb03b08711bc99c71d0288e9e8f43f \ - --hash=sha256:b9056b823aa4f5d4fcd6535b2dee946c3dc9b208453ab46f1f86ab73845be287 \ - --hash=sha256:b98a7c7692b548220fa00dbb0af15a5ecbfe69b29ed46736a056711f9551811b \ - --hash=sha256:b9d8bfce41110f8434cd937bea7cdca17d56f7eb71d52f9663215a921ebad5e8 \ - --hash=sha256:bae747cf7b8d3431c0769e385a326bc94a30ecc7df05f5c66cd8e26bdfb38bfb \ - --hash=sha256:c10e047eaa249b4cd2dc1fec497d8731b799f9232f25d02e1b6a86c4f0aa9221 \ - --hash=sha256:c143fe4cb246de22d22afc6ee71a1d706255ebd05db0e245ade6d3c61a96d3ad \ - --hash=sha256:c1bf64c51302f473d7e252f47434c221acd2a0c78dd7d71a62e11d4442f785ca \ - --hash=sha256:c5352f1a2b7faa194df97fb2071f6ac5de0f13ead4162beba6f45a1d81d20fe3 \ - --hash=sha256:c55830565f53abe7fff9e4a3cea9c4bf828e19da0549b082addff704e2a1b2b3 \ - --hash=sha256:cb0e216f670c57ecee208c859f5b8105a84f18401d43531d1f49fd9a27ac1db0 \ - --hash=sha256:d2bfb1af5af7834d89232628434e0510687e8e34ef0f0e0843999e5f23ee83bc \ - --hash=sha256:d89a8f5e805c4c416591724890cd6dd1f851a9019ac79c2cb3be565b3612d3ce \ - --hash=sha256:df838cb2e41823cf0aed75282b8d071acf40f5513e22713e98bebf9970a080dc \ - --hash=sha256:e14a33e56880d5da83acdac263442ff1175ae7406fa44fe68b6b8ba328511002 \ - --hash=sha256:e15ffa567fbdf4cc1ca3f3e5646849bd61d37196e03b422467674da5280d762f \ - --hash=sha256:e5d945b832b9f61db5840ba01f7f54022780fb2826fd134672e63bcac3e56a0d \ - --hash=sha256:e849538057044cd8ce08c824f9a1c211cfda31c756690ae0f21a068196a0a8f2 \ - --hash=sha256:e8f23a504d1cd5c310f69fbb4638dd5b5a1626b4ebc523fb741716be8c02d4db \ - --hash=sha256:eac07bf101763aae91d1d62622414a7a57f9f9f86bff13851fe0aab56bb36e12 \ - --hash=sha256:eee9ed0a38eb33d2f5e6ba15f71081afe9b167e884f177c6eaf59d3929bbe5e4 \ - --hash=sha256:f3b632ed61b9e19e8ede7e35dc8d483a5650b552d17c25e9fdd9c40f7c9acab8 \ - --hash=sha256:fc11658ff7eb5888e7a2bd5c9a98f73a2e03dbf252f844032d7181627e59017b \ - --hash=sha256:fcb611618d3c902837a0d03fc8c68b7b68cca45c87e588aeae392bc3b92a2a0a - # via -r /tmp/requirementsqzkaj3cw.in -markdown-it-py==4.0.0 \ - --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \ - --hash=sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3 - # via rich -mdurl==0.1.2 \ - --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ - --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba - # via markdown-it-py -meson==1.10.2 \ - --hash=sha256:5f84ef186e6e788d9154db63620fc61b3ece69f643b94b43c8b9203c43d89b36 \ - --hash=sha256:7890287d911dd4ee1ebd0efb61ed0321bfcd87c725df923a837cf90c6508f96b - # via meson-python -meson-python==0.19.0 \ - --hash=sha256:67b5906c37404396d23c195e12c8825506074460d4a2e7083266b845d14f0298 \ - --hash=sha256:9959d198aa69b57fcfd354a34518c6f795b781a73ed0656f4d01660160cc2553 - # via -r /tmp/requirementsqzkaj3cw.in -mypy==1.19.1 \ - --hash=sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd \ - --hash=sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b \ - --hash=sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1 \ - --hash=sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba \ - --hash=sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b \ - --hash=sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045 \ - --hash=sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac \ - --hash=sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6 \ - --hash=sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a \ - --hash=sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24 \ - --hash=sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957 \ - --hash=sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042 \ - --hash=sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e \ - --hash=sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec \ - --hash=sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3 \ - --hash=sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718 \ - --hash=sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f \ - --hash=sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331 \ - --hash=sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1 \ - --hash=sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1 \ - --hash=sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13 \ - --hash=sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67 \ - --hash=sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2 \ - --hash=sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a \ - --hash=sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b \ - --hash=sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8 \ - --hash=sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376 \ - --hash=sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef \ - --hash=sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288 \ - --hash=sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75 \ - --hash=sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74 \ - --hash=sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250 \ - --hash=sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab \ - --hash=sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6 \ - --hash=sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247 \ - --hash=sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925 \ - --hash=sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e \ - --hash=sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e - # via -r /tmp/requirementsqzkaj3cw.in -mypy-extensions==1.1.0 \ - --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ - --hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558 - # via mypy -ninja==1.13.0 \ - --hash=sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f \ - --hash=sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988 \ - --hash=sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9 \ - --hash=sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630 \ - --hash=sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db \ - --hash=sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978 \ - --hash=sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1 \ - --hash=sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72 \ - --hash=sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e \ - --hash=sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2 \ - --hash=sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9 \ - --hash=sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714 \ - --hash=sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200 \ - --hash=sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c \ - --hash=sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5 \ - --hash=sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96 \ - --hash=sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1 \ - --hash=sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa \ - --hash=sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e - # via -r /tmp/requirementsqzkaj3cw.in -nodeenv==1.10.0 \ - --hash=sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 \ - --hash=sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb - # via pyright -numpy==2.4.3 \ - --hash=sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5 \ - --hash=sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc \ - --hash=sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657 \ - --hash=sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd \ - --hash=sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02 \ - --hash=sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a \ - --hash=sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26 \ - --hash=sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92 \ - --hash=sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22 \ - --hash=sha256:23b46bb6d8ecb68b58c09944483c135ae5f0e9b8d8858ece5e4ead783771d2a9 \ - --hash=sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9 \ - --hash=sha256:26952e18d82a1dbbc2f008d402021baa8d6fc8e84347a2072a25e08b46d698b9 \ - --hash=sha256:29363fbfa6f8ee855d7569c96ce524845e3d726d6c19b29eceec7dd555dab152 \ - --hash=sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168 \ - --hash=sha256:2abad5c7fef172b3377502bde47892439bae394a71bc329f31df0fd829b41a9e \ - --hash=sha256:2b3f8d2c4589b1a2028d2a770b0fc4d1f332fb5e01521f4de3199a896d158ddd \ - --hash=sha256:2ddb7919366ee468342b91dea2352824c25b55814a987847b6c52003a7c97f15 \ - --hash=sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093 \ - --hash=sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5 \ - --hash=sha256:33b3bf58ee84b172c067f56aeadc7ee9ab6de69c5e800ab5b10295d54c581adb \ - --hash=sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b \ - --hash=sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd \ - --hash=sha256:48da3a4ee1336454b07497ff7ec83903efa5505792c4e6d9bf83d99dc07a1e18 \ - --hash=sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5 \ - --hash=sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950 \ - --hash=sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e \ - --hash=sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a \ - --hash=sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd \ - --hash=sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4 \ - --hash=sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0 \ - --hash=sha256:61b0cbabbb6126c8df63b9a3a0c4b1f44ebca5e12ff6997b80fcf267fb3150ef \ - --hash=sha256:65f3c2455188f09678355f5cae1f959a06b778bc66d535da07bf2ef20cd319d5 \ - --hash=sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24 \ - --hash=sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476 \ - --hash=sha256:715de7f82e192e8cae5a507a347d97ad17598f8e026152ca97233e3666daaa71 \ - --hash=sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc \ - --hash=sha256:7395e69ff32526710748f92cd8c9849b361830968ea3e24a676f272653e8983e \ - --hash=sha256:76dbb9d4e43c16cf9aa711fcd8de1e2eeb27539dcefb60a1d5e9f12fae1d1ed8 \ - --hash=sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c \ - --hash=sha256:77e76d932c49a75617c6d13464e41203cd410956614d0a0e999b25e9e8d27eec \ - --hash=sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875 \ - --hash=sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a \ - --hash=sha256:7e58765ad74dcebd3ef0208a5078fba32dc8ec3578fe84a604432950cd043d79 \ - --hash=sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f \ - --hash=sha256:8ba7b51e71c05aa1f9bc3641463cd82308eab40ce0d5c7e1fd4038cbf9938147 \ - --hash=sha256:8e236dbda4e1d319d681afcbb136c0c4a8e0f1a5c58ceec2adebb547357fe857 \ - --hash=sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368 \ - --hash=sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720 \ - --hash=sha256:a016db5c5dba78fa8fe9f5d80d6708f9c42ab087a739803c0ac83a43d686a470 \ - --hash=sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b \ - --hash=sha256:a1988292870c7cb9d0ebb4cc96b4d447513a9644801de54606dc7aabf2b7d920 \ - --hash=sha256:a315e5234d88067f2d97e1f2ef670a7569df445d55400f1e33d117418d008d52 \ - --hash=sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73 \ - --hash=sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349 \ - --hash=sha256:abdce0f71dcb4a00e4e77f3faf05e4616ceccfe72ccaa07f47ee79cda3b7b0f4 \ - --hash=sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3 \ - --hash=sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7 \ - --hash=sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee \ - --hash=sha256:bc71942c789ef415a37f0d4eab90341425a00d538cd0642445d30b41023d3395 \ - --hash=sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c \ - --hash=sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611 \ - --hash=sha256:c6b124bfcafb9e8d3ed09130dbee44848c20b3e758b6bbf006e641778927c028 \ - --hash=sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d \ - --hash=sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7 \ - --hash=sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070 \ - --hash=sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0 \ - --hash=sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc \ - --hash=sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687 \ - --hash=sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0 \ - --hash=sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f \ - --hash=sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97 \ - --hash=sha256:eb610595dd91560905c132c709412b512135a60f1851ccbd2c959e136431ff67 - # via -r /tmp/requirementsqzkaj3cw.in -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 - # via - # build - # meson-python - # pyproject-metadata -patchelf==0.17.2.4 \ - --hash=sha256:09fd848d625a165fc7b7e07745508c24077129b019c4415a882938781d43adf8 \ - --hash=sha256:2931a1b5b85f3549661898af7bf746afbda7903c7c9a967cfc998a3563f84fad \ - --hash=sha256:343bb1b94e959f9070ca9607453b04390e36bbaa33c88640b989cefad0aa049e \ - --hash=sha256:680a266a70f60a7a4f4c448482c5bdba80cc8e6bb155a49dcc24238ba49927b0 \ - --hash=sha256:7076d9e127230982e20a81a6e2358d3343004667ba510d9f822d4fdee29b0d71 \ - --hash=sha256:970ee5cd8af33e5ea2099510b2f9013fa1b8d5cd763bf3fd3961281c18101a09 \ - --hash=sha256:ae44cb3c857d50f54b99e5697aa978726ada33a8a6129d4b8b7ffd28b996652d \ - --hash=sha256:d842b51f0401460f3b1f3a3a67d2c266a8f515a5adfbfa6e7b656cb3ac2ed8bc \ - --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 - # via -r /tmp/requirementsqzkaj3cw.in -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 - # via mypy -pip==23.3.2 \ - --hash=sha256:5052d7889c1f9d05224cd41741acb7c5d6fa735ab34e339624a614eaaa7e7d76 \ - --hash=sha256:7fd9972f96db22c8077a1ee2691b172c8089b17a5652a44494a9ecb0d78f9149 - # via -r /tmp/requirementsqzkaj3cw.in -pybind11==3.0.2 \ - --hash=sha256:432f01aeb68e361a3a7fc7575c2c7f497595bf640f747acd909ff238dd766e06 \ - --hash=sha256:f8a6500548919cc33bcd220d5f984688326f574fa97f1107f2f4fdb4c6fb019f - # via -r /tmp/requirementsqzkaj3cw.in -pycparser==3.0 \ - --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ - --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 - # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d - # via - # -r /tmp/requirementsqzkaj3cw.in - # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 - # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 - # via -r /tmp/requirementsqzkaj3cw.in -pygments==2.19.2 \ - --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ - --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b - # via rich -pyproject-hooks==1.2.0 \ - --hash=sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8 \ - --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 - # via build -pyproject-metadata==0.11.0 \ - --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ - --hash=sha256:c72fa49418bb7c5a10f25e050c418009898d1c051721d19f98a6fb6da59a66cf - # via meson-python -pyrefly==0.56.0 \ - --hash=sha256:1e5f53781875024086a5b9f31a89c57d2977487fc3f819d9255008ad34b86fe2 \ - --hash=sha256:21f018f47debc0842b2c3072201e53c138ae32bcda4f3119bfc8d23f59c16b3e \ - --hash=sha256:4683f5e8820d5fbfb84231b643b2c5f6cd40b982cac48ef756d4e3d9b09a39cc \ - --hash=sha256:5478229b09f4bba5bfea000b5ba20ea405f62dc7619ea81197e7ea637d6cba8d \ - --hash=sha256:95366056ceb224571b9f1c20e801d949f2c1fa2cf4ed6ceaadf85ca2ebe6fb27 \ - --hash=sha256:ec6ab3f9e2c03bae8dfa520f52778f47b6762020929a664177d36aa3b941db22 \ - --hash=sha256:f0440a4bbf119ab646468f360e0bd047df051352db1e5d5b9fd58f89e8458809 \ - --hash=sha256:f4948021639288b1ccda5f124c9562dc7f0a2679111eb314fa266c7bfd9f8603 \ - --hash=sha256:f84d21d9b9b58481eea02204e2f73cabb93751b21ab2cd99178b4bde24be6a82 - # via -r /tmp/requirementsqzkaj3cw.in -pyright==1.1.408 \ - --hash=sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1 \ - --hash=sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684 - # via -r /tmp/requirementsqzkaj3cw.in -python-dotenv==1.2.2 \ - --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ - --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 - # via pydantic-settings -pyyaml==6.0.3 \ - --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ - --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ - --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ - --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ - --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ - --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ - --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ - --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ - --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ - --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ - --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ - --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ - --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ - --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ - --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ - --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ - --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ - --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ - --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ - --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ - --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ - --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ - --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ - --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ - --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ - --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ - --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ - --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ - --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ - --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ - --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ - --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ - --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ - --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ - --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ - --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ - --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ - --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ - --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ - --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ - --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ - --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ - --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ - --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ - --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ - --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ - --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ - --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ - --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ - --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ - --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ - --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ - --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ - --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ - --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ - --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ - --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ - --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ - --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ - --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ - --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ - --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ - --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ - --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ - --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ - --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ - --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ - --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ - --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ - --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ - --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ - --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ - --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 - # via yq -regex==2026.2.28 \ - --hash=sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1 \ - --hash=sha256:00f2b8d9615aa165fdff0a13f1a92049bfad555ee91e20d246a51aa0b556c60a \ - --hash=sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4 \ - --hash=sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d \ - --hash=sha256:03a83cc26aa2acda6b8b9dfe748cf9e84cbd390c424a1de34fdcef58961a297a \ - --hash=sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911 \ - --hash=sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952 \ - --hash=sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b \ - --hash=sha256:0d5bef2031cbf38757a0b0bc4298bb4824b6332d28edc16b39247228fbdbad97 \ - --hash=sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25 \ - --hash=sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8 \ - --hash=sha256:195237dc327858a7721bf8b0bbbef797554bc13563c3591e91cd0767bacbe359 \ - --hash=sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff \ - --hash=sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a \ - --hash=sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7 \ - --hash=sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0 \ - --hash=sha256:1f8b17be5c27a684ea6759983c13506bd77bfc7c0347dff41b18ce5ddd2ee09a \ - --hash=sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215 \ - --hash=sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43 \ - --hash=sha256:2954379dd20752e82d22accf3ff465311cbb2bac6c1f92c4afd400e1757f7451 \ - --hash=sha256:2afa673660928d0b63d84353c6c08a8a476ddfc4a47e11742949d182e6863ce8 \ - --hash=sha256:2b2b23587b26496ff5fd40df4278becdf386813ec00dc3533fa43a4cf0e2ad3c \ - --hash=sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b \ - --hash=sha256:3935174fa4d9f70525a4367aaff3cb8bc0548129d114260c29d9dfa4a5b41692 \ - --hash=sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e \ - --hash=sha256:3b24bd7e9d85dc7c6a8bd2aa14ecd234274a0248335a02adeb25448aecdd420d \ - --hash=sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae \ - --hash=sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8 \ - --hash=sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11 \ - --hash=sha256:50c2fc924749543e0eacc93ada6aeeb3ea5f6715825624baa0dccaec771668ae \ - --hash=sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5 \ - --hash=sha256:516604edd17b1c2c3e579cf4e9b25a53bf8fa6e7cedddf1127804d3e0140ca64 \ - --hash=sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472 \ - --hash=sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18 \ - --hash=sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a \ - --hash=sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd \ - --hash=sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d \ - --hash=sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d \ - --hash=sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9 \ - --hash=sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96 \ - --hash=sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784 \ - --hash=sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b \ - --hash=sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff \ - --hash=sha256:73cdcdbba8028167ea81490c7f45280113e41db2c7afb65a276f4711fa3bcbff \ - --hash=sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc \ - --hash=sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf \ - --hash=sha256:7ab218076eb0944549e7fe74cf0e2b83a82edb27e81cc87411f76240865e04d5 \ - --hash=sha256:7c1b34dfa72f826f535b20712afa9bb3ba580020e834f3c69866c5bddbf10098 \ - --hash=sha256:851fa70df44325e1e4cdb79c5e676e91a78147b1b543db2aec8734d2add30ec2 \ - --hash=sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05 \ - --hash=sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf \ - --hash=sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6 \ - --hash=sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768 \ - --hash=sha256:948c12ef30ecedb128903c2c2678b339746eb7c689c5c21957c4a23950c96d15 \ - --hash=sha256:94d63db12e45a9b9f064bfe4800cefefc7e5f182052e4c1b774d46a40ab1d9bb \ - --hash=sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881 \ - --hash=sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f \ - --hash=sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8 \ - --hash=sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c \ - --hash=sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d \ - --hash=sha256:9dd450db6458387167e033cfa80887a34c99c81d26da1bf8b0b41bf8c9cac88e \ - --hash=sha256:a25c7701e4f7a70021db9aaf4a4a0a67033c6318752146e03d1b94d32006217e \ - --hash=sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341 \ - --hash=sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e \ - --hash=sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2 \ - --hash=sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550 \ - --hash=sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e \ - --hash=sha256:b387a0d092dac157fb026d737dde35ff3e49ef27f285343e7c6401851239df27 \ - --hash=sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8 \ - --hash=sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59 \ - --hash=sha256:b49eb78048c6354f49e91e4b77da21257fecb92256b6d599ae44403cab30b05b \ - --hash=sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3 \ - --hash=sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117 \ - --hash=sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc \ - --hash=sha256:ba55c50f408fb5c346a3a02d2ce0ebc839784e24f7c9684fde328ff063c3cdea \ - --hash=sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b \ - --hash=sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e \ - --hash=sha256:bcb399ed84eabf4282587ba151f2732ad8168e66f1d3f85b1d038868fe547703 \ - --hash=sha256:bd477d5f79920338107f04aa645f094032d9e3030cc55be581df3d1ef61aa318 \ - --hash=sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2 \ - --hash=sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952 \ - --hash=sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944 \ - --hash=sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7 \ - --hash=sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b \ - --hash=sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033 \ - --hash=sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4 \ - --hash=sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8 \ - --hash=sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f \ - --hash=sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d \ - --hash=sha256:dd8847c4978bc3c7e6c826fb745f5570e518b8459ac2892151ce6627c7bc00d5 \ - --hash=sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d \ - --hash=sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec \ - --hash=sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b \ - --hash=sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a \ - --hash=sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92 \ - --hash=sha256:e621fb7c8dc147419b28e1702f58a0177ff8308a76fa295c71f3e7827849f5d9 \ - --hash=sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc \ - --hash=sha256:e7ce83654d1ab701cb619285a18a8e5a889c1216d746ddc710c914ca5fd71022 \ - --hash=sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6 \ - --hash=sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c \ - --hash=sha256:ec6f5674c5dc836994f50f1186dd1fafde4be0666aae201ae2fcc3d29d8adf27 \ - --hash=sha256:edb1b1b3a5576c56f08ac46f108c40333f222ebfd5cf63afdfa3aab0791ebe5b \ - --hash=sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc \ - --hash=sha256:f2791948f7c70bb9335a9102df45e93d428f4b8128020d85920223925d73b9e1 \ - --hash=sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07 \ - --hash=sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c \ - --hash=sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a \ - --hash=sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33 \ - --hash=sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95 \ - --hash=sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081 \ - --hash=sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d \ - --hash=sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7 \ - --hash=sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb \ - --hash=sha256:fd63453f10d29097cc3dc62d070746523973fb5aa1c66d25f8558bebd47fed61 - # via toml-cli -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b - # via typer -ruff==0.15.6 \ - --hash=sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8 \ - --hash=sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab \ - --hash=sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb \ - --hash=sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9 \ - --hash=sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512 \ - --hash=sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15 \ - --hash=sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c \ - --hash=sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff \ - --hash=sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4 \ - --hash=sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb \ - --hash=sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0 \ - --hash=sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e \ - --hash=sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406 \ - --hash=sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c \ - --hash=sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837 \ - --hash=sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0 \ - --hash=sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e \ - --hash=sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3 - # via -r /tmp/requirementsqzkaj3cw.in -setuptools==82.0.1 \ - --hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \ - --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb - # via -r /tmp/requirementsqzkaj3cw.in -shellingham==1.5.4 \ - --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ - --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de - # via typer -solv==0.7.35 \ - --hash=sha256:05aaa3abaf05482d22946cab81e328b536a59465cc47d628a72bc3d2e4dcafac \ - --hash=sha256:6233b5afe84c22ab6d8e08b099b2d7642f51fcad88d8039e35b4bbaf791d9705 \ - --hash=sha256:8186e0f74a999073de13ed77109394be21c22c532fa2836ef2f5a01c65b0081c \ - --hash=sha256:91f04e2126a98948bf267941652cdc1694ac841777268041606f14996c0a6434 \ - --hash=sha256:9b27cd3e7803f3de220634107f5be3dab87ebd01211a8e1444f91216fbceb470 \ - --hash=sha256:ac360dacc63ebe018418be314406e39cd24695e1cfbb88c542a2944ccc4714f0 \ - --hash=sha256:b1597b068780aaf67a8aa635ae0fb5a861046fb956f18db15a5b01caef37a3e0 \ - --hash=sha256:bdfdcede46c037bed08352e69dd0859080155f1c11605a4efa2417c8e8c30329 \ - --hash=sha256:c5fd4e566af56e18b8d7eb75557416fdad731b0c7254fe5973d77b92f769dc02 \ - --hash=sha256:ca74a2a899804c55a316f2dbc4a942de45a2767b8e36b0f7747700ea67aabc06 \ - --hash=sha256:d5606cd8e4bb1b3f30bbff592632fbc47c0e4f79d42869eff1af78dca8e6fd47 \ - --hash=sha256:e2fc4cd373f554c4d934e8df3ad3d834fb7be4c90e665691bd4b26fb1e95c542 \ - --hash=sha256:f24f7c8012a674c56f055f7c6e1d3338a8a5d6ec0d8710a9387eccc97557e422 - # via -r /tmp/requirementsqzkaj3cw.in -toml-cli==0.8.2 \ - --hash=sha256:7af4679ca04c53ad0f6d300dab26f45a78fedf88e8310305bfe0a8ead37fd000 \ - --hash=sha256:ec61fbfe5f0d6f2d6bfd9e1127cad38a797b8685a93589f2e22efd168b0cc866 - # via -r /tmp/requirementsqzkaj3cw.in -tomli==2.4.0 \ - --hash=sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729 \ - --hash=sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b \ - --hash=sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d \ - --hash=sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df \ - --hash=sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576 \ - --hash=sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d \ - --hash=sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1 \ - --hash=sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a \ - --hash=sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e \ - --hash=sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc \ - --hash=sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702 \ - --hash=sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6 \ - --hash=sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd \ - --hash=sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4 \ - --hash=sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776 \ - --hash=sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a \ - --hash=sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66 \ - --hash=sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87 \ - --hash=sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2 \ - --hash=sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f \ - --hash=sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475 \ - --hash=sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f \ - --hash=sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95 \ - --hash=sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9 \ - --hash=sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3 \ - --hash=sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9 \ - --hash=sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76 \ - --hash=sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da \ - --hash=sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8 \ - --hash=sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51 \ - --hash=sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86 \ - --hash=sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8 \ - --hash=sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0 \ - --hash=sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b \ - --hash=sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1 \ - --hash=sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e \ - --hash=sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d \ - --hash=sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c \ - --hash=sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867 \ - --hash=sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a \ - --hash=sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c \ - --hash=sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0 \ - --hash=sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4 \ - --hash=sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614 \ - --hash=sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132 \ - --hash=sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa \ - --hash=sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087 - # via -r /tmp/requirementsqzkaj3cw.in -tomlkit==0.14.0 \ - --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ - --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 - # via - # -r /tmp/requirementsqzkaj3cw.in - # toml-cli - # yq -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 - # via toml-cli -typing-extensions==4.15.0 \ - --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ - --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 - # via - # mypy - # pydantic - # pydantic-core - # pyright - # typing-inspection -typing-inspection==0.4.2 \ - --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \ - --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464 - # via - # pydantic - # pydantic-settings -uv==0.10.10 \ - --hash=sha256:009a4c534e83bada52c8e2cccea6250e3486d01d609e4eb874cd302e2e534269 \ - --hash=sha256:1085cc907a1315002015bc218cc88e42c5171a03a705421341cdb420400ee2f3 \ - --hash=sha256:266b24bf85aa021af37d3fb22d84ef40746bc4da402e737e365b12badff60e89 \ - --hash=sha256:2c89017c0532224dc1ec6f3be1bc4ec3d8c3f291c23a229e8a40e3cc5828f599 \ - --hash=sha256:3873b965d62b282ab51e328f4b15a760b32b11a7231dc3fe658fa11d98f20136 \ - --hash=sha256:3da90c197e8e9f5d49862556fa9f4a9dd5b8617c0bbcc88585664e777209a315 \ - --hash=sha256:41a3cc94e0c43070e48a521b6b26156ffde1cdc2088339891aa35eb2245ac5cf \ - --hash=sha256:49235f8a745ef10eea24b2f07be1ee77da056792cef897630b78c391c5f1e2e4 \ - --hash=sha256:4f9fd7f62df91c2d91c02e2039d4c5bad825077d04ebd27af8ea35a8cc736daf \ - --hash=sha256:52e8b70a4fd7a734833c6a55714b679a10b29cf69b2e663e657df1995cf11c6a \ - --hash=sha256:5dd85cc8ff9fa967c02c3edbf2b77d54b56bedcb56b323edec0df101f37f26e2 \ - --hash=sha256:8a59c80ade3aa20baf9ec5d17b6449f4fdba9212f6e3d1bdf2a6db94cbc64c21 \ - --hash=sha256:8f56734baf7a8bd616da69cd7effe1a237c2cb364ec4feefe6a4b180f1cf5ec2 \ - --hash=sha256:9c863fb46a62f3c8a1b7bc1520b0939c05cf4fab06e7233fc48ed17538e6601e \ - --hash=sha256:e42e9e4a196ef75d1089715574eb1fe9bb62d390da05c6c8b36650a4de23d59f \ - --hash=sha256:e77e52ba74e0085a1c03a16611146c6f813034787f83a2fd260cdc8357e18d2d \ - --hash=sha256:ee47b5bc1b8ccd246a3801611b2b71c8107db3a2b528e64463d737fd8e4f2798 \ - --hash=sha256:f97709570158efc87d52ddca90f2c96293eea382d81be295b1fd7088153d6a83 \ - --hash=sha256:fbd827042dbdcadeb5e3418bee73ded9feb5ead8edac23e6e1b5dadb5a90f8b2 - # via -r /tmp/requirementsqzkaj3cw.in -xmltodict==1.0.4 \ - --hash=sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61 \ - --hash=sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a - # via yq -yq==3.4.3 \ - --hash=sha256:547e34bc3caacce83665fd3429bf7c85f8e8b6b9aaee3f953db1ad716ff3434d \ - --hash=sha256:ba586a1a6f30cf705b2f92206712df2281cd320280210e7b7b80adcb8f256e3b - # via -r /tmp/requirementsqzkaj3cw.in diff --git a/releases/whl/online_fxreader_pr34-0.1.5.18-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.18-py3-none-any.whl new file mode 100644 index 0000000..eac0045 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.18-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4da989c2c7216dc258fe74ca28a5e80413f410d8660502da81370293e8bc56d3 +size 72213 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.19-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.19-py3-none-any.whl new file mode 100644 index 0000000..07d5c9b --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.19-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c41fac5f3f04295d11aff5a55783580c39a2bbf783c24434af9eab82ecaf7830 +size 72256 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.20-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.20-py3-none-any.whl new file mode 100644 index 0000000..14cad4f --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.20-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a04a4880e3887208da31969c5b5158a3a51f83627a752393ebd8b72684ec1a1 +size 72294 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.21-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.21-py3-none-any.whl new file mode 100644 index 0000000..5f3183d --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.21-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b2f7670ef865966367e81427c1e97d2eb538962b77b1cfc207356f6e91e65c1 +size 72296 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.22-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.22-py3-none-any.whl new file mode 100644 index 0000000..ddd7cd1 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.22-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58bebb4c696cb36c24ec89e848785bcea89ec3b261f97c54ce8b350626785b98 +size 72324 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.23-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.23-py3-none-any.whl new file mode 100644 index 0000000..7141429 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.23-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:584d16efd888caace6884bed15449514d48ca670d46d16dff0675ac54642d429 +size 72346 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl new file mode 100644 index 0000000..45b043b --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.24-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e6ad1193bbdb859c2f0b1b407ccf944880212fd339139beede1eb31b4b2ce8a +size 73447 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl new file mode 100644 index 0000000..a1d2890 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.25-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b70a3fb2fe6652c1ff8c14e464a5baf80a5d9da9e39204eeba40490bf13deca +size 74822 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl new file mode 100644 index 0000000..58b307f --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.26-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3066558ef4a5dd2dfda85c3d9fb7d50014774d500187da86c6cc0795fca3be10 +size 74831 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl new file mode 100644 index 0000000..beaf03f --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.27-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d081758fdb91fb460da5c55d3a38257122de096363a8d956ba2f91a234566010 +size 74850 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.28-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.28-py3-none-any.whl new file mode 100644 index 0000000..e1397f2 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.28-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dedd1d53da278e01078df915f3b574aa81bd5af39bad5f815cd2b332bd2cfe75 +size 74933 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.29-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.29-py3-none-any.whl new file mode 100644 index 0000000..c691957 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.29-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a03f843dc71d1aac335daa628e8ef4aff46aa0f31b224bbf5762bd1456e799c7 +size 75106 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.30-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.30-py3-none-any.whl new file mode 100644 index 0000000..aa31e53 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.30-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:106beb7b687f45404e77c3d05de892d8d51e54e8471cd6a00e714b73fb1010f2 +size 75109 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.31-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.31-py3-none-any.whl new file mode 100644 index 0000000..8478376 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.31-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d995e0a026bce305e30d458eee51725c6209d44c5a859ee4e5dbaf19b25be3be +size 75089 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.32-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.32-py3-none-any.whl new file mode 100644 index 0000000..6bfe450 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.32-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15752fc356bb4c6b4a92a71b7f615e0c0c2b42df1efb82f8613aa0951a74ae5e +size 75107 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.33-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.33-py3-none-any.whl new file mode 100644 index 0000000..039f555 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.33-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fbaa95dcd0d0c0564fb6b7c161f486b54c4fb591593c769b6ad4fdf08b869f1 +size 75933 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.34-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.34-py3-none-any.whl new file mode 100644 index 0000000..591533d --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.34-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b02999a55ca5687040b6947dd1f66aaafb33b1f5fd28cbd630c2e614690a3b8c +size 75937 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.35-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.35-py3-none-any.whl new file mode 100644 index 0000000..58bebc4 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.35-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f8b32e3c34e253e7cab74bb3d4e772e69b8378e3698e3266204df54d6b1545a +size 75992 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.36-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.36-py3-none-any.whl new file mode 100644 index 0000000..472ebb8 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.36-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6670bd5dea3c54fca9766690b40c8416d7f5ec9d0c0abf9aa5309ec6779caad9 +size 75999 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.37-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.37-py3-none-any.whl new file mode 100644 index 0000000..23b4ecb --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.37-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f92cb068a9a3145aad3ed9e6f66069e7560df1c60982d1ff6fb87d5ce08563c7 +size 76038 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.38-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.38-py3-none-any.whl new file mode 100644 index 0000000..ffe375f --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.38-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3748856fb9d1388a8b419ffa2b3dd523db371a9771dc50539ceb6eee2d395f7 +size 76042 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl new file mode 100644 index 0000000..b68f2ae --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.39-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3f2973ec7d90c44bad04cbfca37cfe065ba2533f4b0320f82153fb439a96739 +size 76124 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.40-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.40-py3-none-any.whl new file mode 100644 index 0000000..7aeca1e --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.40-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a43f4743a00411371d52197d82d7573e9eb34b8a54a343398f9af1b267d8a215 +size 76224 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.41-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.41-py3-none-any.whl new file mode 100644 index 0000000..a6087f6 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.41-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b302323f35f1ed0f351e171be3d54257762a7a816644652eb4925b96bc8007c +size 76304 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.42-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.42-py3-none-any.whl new file mode 100644 index 0000000..97296d9 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.42-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0404b4c65982bc74364d2f07d3bfccbe46bc2fbc64ff10820a383f96c39420a1 +size 80608 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.43-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.43-py3-none-any.whl new file mode 100644 index 0000000..af632a1 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.43-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:faeea76f81f8658b0fa511c3d3d20480ce3f22ceac0318704fe304018cb966e9 +size 82857 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.44-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.44-py3-none-any.whl new file mode 100644 index 0000000..675a8be --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.44-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07bcc23549d0486731a95c7a9905c0bfdeef3024df3c65492faa7c9c76d00b11 +size 82910 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.45-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.45-py3-none-any.whl new file mode 100644 index 0000000..f431de1 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.45-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2453a83bddeb241769a350598b395fcfd25687012c7b2c0ea661a6eeb12776dc +size 82964 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.47-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.47-py3-none-any.whl new file mode 100644 index 0000000..8ac0b29 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.47-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1c6e293c1d065bf0b4c95ee7d5737cde47a0af89d270a14547b544f3f88d770 +size 83100 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.48-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.48-py3-none-any.whl new file mode 100644 index 0000000..975de8b --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.48-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1757771a00bda395b3a7fe8d7b0e186ebd2e85fd3155bc0efe6bdf8676e8d0ef +size 83157 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.49-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.49-py3-none-any.whl new file mode 100644 index 0000000..060311a --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.49-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7f6fa731ebe094aebdaed8aeec4d424f536cf25205b7a9fcae744f717926404 +size 83159 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.50-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.50-py3-none-any.whl new file mode 100644 index 0000000..f183df4 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.50-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43a3ab5b8144e3f0fb9370119152fae5733dd545a7477360f290de68227204d2 +size 83153 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.51-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.51-py3-none-any.whl new file mode 100644 index 0000000..f5d9d86 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.51-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8151ce2349503ea794c7686334a2165a277f06ab44785a7932727abbbf10c20f +size 83251 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.52-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.52-py3-none-any.whl new file mode 100644 index 0000000..6615bdd --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.52-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:115f3772c7ce52f9eaa64082f40bbfcfb6a4e53a787f7145641df56735255493 +size 83260 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.53-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.53-py3-none-any.whl new file mode 100644 index 0000000..c66ba67 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.53-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba1c1ceb2f20f57d3446e965f9f22c1e24041b7d76fa8d4d58a2a8f094b98240 +size 83288 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.54-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.54-py3-none-any.whl new file mode 100644 index 0000000..e646d23 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.54-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f32e106209f3f8c1fc29c9d17c19ad473f8f1ce0af4d90df939beb8a1a56280 +size 83298 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.56-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.56-py3-none-any.whl new file mode 100644 index 0000000..d285dbc --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.56-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:203c6d7ad0dc5be6c5e41d2648d47d31b1c7bea27826b23c59628c2d096b74e9 +size 83484 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.57-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.57-py3-none-any.whl new file mode 100644 index 0000000..74f6399 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.57-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52148600329e51ad1a6405600ee21449e9ba30673fe4e4762a6ca018e718dcf7 +size 83489 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.58-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.58-py3-none-any.whl new file mode 100644 index 0000000..41ce28c --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.58-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0979c942270fe8406aa3ec18ead07d2469a3d6737b1b677ccb7c4675e63be512 +size 83525 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.59-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.59-py3-none-any.whl new file mode 100644 index 0000000..9c1361a --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.59-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6369fdafe47a5fed54e603afbd214d3519cf0f126cdd44e92abee3ab2424c62b +size 83529 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.60-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.60-py3-none-any.whl new file mode 100644 index 0000000..cb77839 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.60-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e13d2ecf1b68152400fd11c95abd6ddbb526feac6507f8d4d8d61ecfda2e027c +size 83552 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.61-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.61-py3-none-any.whl new file mode 100644 index 0000000..4ddbbad --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.61-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d944b0a23cd0c908213a46490490e7b8c449fb6b2d46eb47b723c1a6c3b8e199 +size 83652 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.62-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.62-py3-none-any.whl new file mode 100644 index 0000000..33d73c5 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.62-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9f4f014d445f274cc53acf18ca9701dd515f2a61d82caedc3598857814dc1b0 +size 83657 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.63-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.63-py3-none-any.whl new file mode 100644 index 0000000..e754139 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.63-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92b0db4b8e9e896b2acb9545cb08fc0441e6bb73e2b7e7d7cdc69e5820af0ac1 +size 83695 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.64-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.64-py3-none-any.whl new file mode 100644 index 0000000..30f5648 --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.64-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51dbc93696d697834ddaca9d121fbd8f01a7ef03bdee02387be32c7401549585 +size 83715 diff --git a/releases/whl/online_fxreader_pr34-0.1.5.65-py3-none-any.whl b/releases/whl/online_fxreader_pr34-0.1.5.65-py3-none-any.whl new file mode 100644 index 0000000..9b317ab --- /dev/null +++ b/releases/whl/online_fxreader_pr34-0.1.5.65-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29325361cdf449311627eee6abffbec2887dbef352d37c43d855a07f3bb78496 +size 83820 diff --git a/requirements.in b/requirements.in deleted file mode 100644 index b022ae5..0000000 --- a/requirements.in +++ /dev/null @@ -1,6 +0,0 @@ -mypy -ruff -numpy -pydantic -requests -types-requests diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 87a4bad..0000000 --- a/requirements.txt +++ /dev/null @@ -1,350 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile --generate-hashes requirements.in -annotated-types==0.7.0 \ - --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ - --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 - # via pydantic -certifi==2025.1.31 \ - --hash=sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651 \ - --hash=sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe - # via requests -charset-normalizer==3.4.1 \ - --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ - --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ - --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ - --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ - --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ - --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ - --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ - --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ - --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ - --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ - --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ - --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ - --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ - --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ - --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ - --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ - --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ - --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ - --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ - --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ - --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ - --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ - --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ - --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ - --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ - --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ - --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ - --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \ - --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ - --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ - --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ - --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ - --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ - --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ - --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ - --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ - --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ - --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ - --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ - --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ - --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ - --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ - --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ - --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ - --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ - --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ - --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ - --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ - --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ - --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ - --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ - --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ - --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ - --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ - --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ - --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ - --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ - --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ - --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ - --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ - --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ - --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ - --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ - --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ - --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ - --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ - --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ - --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ - --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ - --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ - --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ - --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ - --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ - --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ - --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ - --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ - --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ - --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ - --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ - --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ - --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ - --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ - --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ - --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ - --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ - --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ - --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ - --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ - --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ - --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ - --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ - --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 - # via requests -idna==3.10 \ - --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ - --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 - # via requests -mypy==1.15.0 \ - --hash=sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e \ - --hash=sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22 \ - --hash=sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f \ - --hash=sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2 \ - --hash=sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f \ - --hash=sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b \ - --hash=sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5 \ - --hash=sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f \ - --hash=sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43 \ - --hash=sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e \ - --hash=sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c \ - --hash=sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828 \ - --hash=sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba \ - --hash=sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee \ - --hash=sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d \ - --hash=sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b \ - --hash=sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445 \ - --hash=sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e \ - --hash=sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13 \ - --hash=sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5 \ - --hash=sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd \ - --hash=sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf \ - --hash=sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357 \ - --hash=sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b \ - --hash=sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036 \ - --hash=sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559 \ - --hash=sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3 \ - --hash=sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f \ - --hash=sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464 \ - --hash=sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980 \ - --hash=sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078 \ - --hash=sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5 - # via -r requirements.in -mypy-extensions==1.0.0 \ - --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ - --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 - # via mypy -numpy==2.2.4 \ - --hash=sha256:05c076d531e9998e7e694c36e8b349969c56eadd2cdcd07242958489d79a7286 \ - --hash=sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542 \ - --hash=sha256:11c43995255eb4127115956495f43e9343736edb7fcdb0d973defd9de14cd84f \ - --hash=sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d \ - --hash=sha256:1974afec0b479e50438fc3648974268f972e2d908ddb6d7fb634598cdb8260a0 \ - --hash=sha256:1cf4e5c6a278d620dee9ddeb487dc6a860f9b199eadeecc567f777daace1e9e7 \ - --hash=sha256:207a2b8441cc8b6a2a78c9ddc64d00d20c303d79fba08c577752f080c4007ee3 \ - --hash=sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3 \ - --hash=sha256:2aad3c17ed2ff455b8eaafe06bcdae0062a1db77cb99f4b9cbb5f4ecb13c5146 \ - --hash=sha256:2fa8fa7697ad1646b5c93de1719965844e004fcad23c91228aca1cf0800044a1 \ - --hash=sha256:31504f970f563d99f71a3512d0c01a645b692b12a63630d6aafa0939e52361e6 \ - --hash=sha256:3387dd7232804b341165cedcb90694565a6015433ee076c6754775e85d86f1fc \ - --hash=sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9 \ - --hash=sha256:4f92084defa704deadd4e0a5ab1dc52d8ac9e8a8ef617f3fbb853e79b0ea3592 \ - --hash=sha256:65ef3468b53269eb5fdb3a5c09508c032b793da03251d5f8722b1194f1790c00 \ - --hash=sha256:6f527d8fdb0286fd2fd97a2a96c6be17ba4232da346931d967a0630050dfd298 \ - --hash=sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8 \ - --hash=sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2 \ - --hash=sha256:79bd5f0a02aa16808fcbc79a9a376a147cc1045f7dfe44c6e7d53fa8b8a79392 \ - --hash=sha256:7a4e84a6283b36632e2a5b56e121961f6542ab886bc9e12f8f9818b3c266bfbb \ - --hash=sha256:8120575cb4882318c791f839a4fd66161a6fa46f3f0a5e613071aae35b5dd8f8 \ - --hash=sha256:81413336ef121a6ba746892fad881a83351ee3e1e4011f52e97fba79233611fd \ - --hash=sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9 \ - --hash=sha256:879cf3a9a2b53a4672a168c21375166171bc3932b7e21f622201811c43cdd3b0 \ - --hash=sha256:892c10d6a73e0f14935c31229e03325a7b3093fafd6ce0af704be7f894d95687 \ - --hash=sha256:92bda934a791c01d6d9d8e038363c50918ef7c40601552a58ac84c9613a665bc \ - --hash=sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f \ - --hash=sha256:9eeea959168ea555e556b8188da5fa7831e21d91ce031e95ce23747b7609f8a4 \ - --hash=sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a \ - --hash=sha256:a761ba0fa886a7bb33c6c8f6f20213735cb19642c580a931c625ee377ee8bd39 \ - --hash=sha256:a7b9084668aa0f64e64bd00d27ba5146ef1c3a8835f3bd912e7a9e01326804c4 \ - --hash=sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775 \ - --hash=sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c \ - --hash=sha256:ac0280f1ba4a4bfff363a99a6aceed4f8e123f8a9b234c89140f5e894e452ecd \ - --hash=sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020 \ - --hash=sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d \ - --hash=sha256:bb649f8b207ab07caebba230d851b579a3c8711a851d29efe15008e31bb4de24 \ - --hash=sha256:bce43e386c16898b91e162e5baaad90c4b06f9dcbe36282490032cec98dc8ae7 \ - --hash=sha256:bd3ad3b0a40e713fc68f99ecfd07124195333f1e689387c180813f0e94309d6f \ - --hash=sha256:c3f7ac96b16955634e223b579a3e5798df59007ca43e8d451a0e6a50f6bfdfba \ - --hash=sha256:cf28633d64294969c019c6df4ff37f5698e8326db68cc2b66576a51fad634880 \ - --hash=sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d \ - --hash=sha256:db1f1c22173ac1c58db249ae48aa7ead29f534b9a948bc56828337aa84a32ed6 \ - --hash=sha256:dbe512c511956b893d2dacd007d955a3f03d555ae05cfa3ff1c1ff6df8851854 \ - --hash=sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017 \ - --hash=sha256:e2f085ce2e813a50dfd0e01fbfc0c12bbe5d2063d99f8b29da30e544fb6483b8 \ - --hash=sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae \ - --hash=sha256:e9e0a277bb2eb5d8a7407e14688b85fd8ad628ee4e0c7930415687b6564207a4 \ - --hash=sha256:ea2bb7e2ae9e37d96835b3576a4fa4b3a97592fbea8ef7c3587078b0068b8f09 \ - --hash=sha256:ee4d528022f4c5ff67332469e10efe06a267e32f4067dc76bb7e2cddf3cd25ff \ - --hash=sha256:f05d4198c1bacc9124018109c5fba2f3201dbe7ab6e92ff100494f236209c960 \ - --hash=sha256:f34dc300df798742b3d06515aa2a0aee20941c13579d7a2f2e10af01ae4901ee \ - --hash=sha256:f4162988a360a29af158aeb4a2f4f09ffed6a969c9776f8f3bdee9b06a8ab7e5 \ - --hash=sha256:f486038e44caa08dbd97275a9a35a283a8f1d2f0ee60ac260a1790e76660833c \ - --hash=sha256:f7de08cbe5551911886d1ab60de58448c6df0f67d9feb7d1fb21e9875ef95e91 - # via -r requirements.in -pydantic==2.10.6 \ - --hash=sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584 \ - --hash=sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236 - # via -r requirements.in -pydantic-core==2.27.2 \ - --hash=sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278 \ - --hash=sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50 \ - --hash=sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9 \ - --hash=sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f \ - --hash=sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6 \ - --hash=sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc \ - --hash=sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54 \ - --hash=sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630 \ - --hash=sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9 \ - --hash=sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236 \ - --hash=sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7 \ - --hash=sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee \ - --hash=sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b \ - --hash=sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048 \ - --hash=sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc \ - --hash=sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130 \ - --hash=sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4 \ - --hash=sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd \ - --hash=sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4 \ - --hash=sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7 \ - --hash=sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7 \ - --hash=sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4 \ - --hash=sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e \ - --hash=sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa \ - --hash=sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6 \ - --hash=sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962 \ - --hash=sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b \ - --hash=sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f \ - --hash=sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474 \ - --hash=sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5 \ - --hash=sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459 \ - --hash=sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf \ - --hash=sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a \ - --hash=sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c \ - --hash=sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76 \ - --hash=sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362 \ - --hash=sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4 \ - --hash=sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934 \ - --hash=sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320 \ - --hash=sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118 \ - --hash=sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96 \ - --hash=sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306 \ - --hash=sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046 \ - --hash=sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3 \ - --hash=sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2 \ - --hash=sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af \ - --hash=sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9 \ - --hash=sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67 \ - --hash=sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a \ - --hash=sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27 \ - --hash=sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35 \ - --hash=sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b \ - --hash=sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151 \ - --hash=sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b \ - --hash=sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154 \ - --hash=sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133 \ - --hash=sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef \ - --hash=sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145 \ - --hash=sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15 \ - --hash=sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4 \ - --hash=sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc \ - --hash=sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee \ - --hash=sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c \ - --hash=sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0 \ - --hash=sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5 \ - --hash=sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57 \ - --hash=sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b \ - --hash=sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8 \ - --hash=sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1 \ - --hash=sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da \ - --hash=sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e \ - --hash=sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc \ - --hash=sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993 \ - --hash=sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656 \ - --hash=sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4 \ - --hash=sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c \ - --hash=sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb \ - --hash=sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d \ - --hash=sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9 \ - --hash=sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e \ - --hash=sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1 \ - --hash=sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc \ - --hash=sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a \ - --hash=sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9 \ - --hash=sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506 \ - --hash=sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b \ - --hash=sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1 \ - --hash=sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d \ - --hash=sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99 \ - --hash=sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3 \ - --hash=sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31 \ - --hash=sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c \ - --hash=sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39 \ - --hash=sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a \ - --hash=sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308 \ - --hash=sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2 \ - --hash=sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228 \ - --hash=sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b \ - --hash=sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9 \ - --hash=sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad - # via pydantic -requests==2.32.3 \ - --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ - --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 - # via -r requirements.in -ruff==0.11.1 \ - --hash=sha256:111dbad1706d8200a7138237b4766b45ba7ee45cc8299c02102f4327624f86a2 \ - --hash=sha256:1f2b03d504516d6b22065ce7fac2564dac15d79a6a776452dabfdd7673a45b07 \ - --hash=sha256:28e2d89e7ba8a1525cdb50bc86c07aba35e7bbeef86dad93781b14ad94dc732c \ - --hash=sha256:429a2e533e3a0dba2ba7e0608a736e728150aa9b6d179245aa11a1339baa968b \ - --hash=sha256:441f94c44fe250691c92382ef84f40acef290766fb3e819a9035e83eadd4dbbe \ - --hash=sha256:52b95a9071f5ad8552af890bd814c6a04daf5b27297ac1054e3667016f3ab739 \ - --hash=sha256:62882a4cc7c0a48c2f34189bd4c7ba45f3d0efb990e02413eeb180aa042a39ca \ - --hash=sha256:6bbcc2984a4d5cbc0f7b10409e74a00a043be45d813e5e81eb58e707455df7f1 \ - --hash=sha256:7aa939fa57ef6770d18bd5cf0d6de77198dd762a559bd0d4a8763bdae4c8cc16 \ - --hash=sha256:88d9c283ebc88faa5bc23fa33f399b6d47a93f5980c92edcddf1f2127aa376b3 \ - --hash=sha256:9c833671aaefcbe280aa54da387264402ffbb1e513ff3420c9c7265ea56d6c5c \ - --hash=sha256:a5a57cd457764228c73066b832040728b02a3837c53c8a781a960b68129c4e0b \ - --hash=sha256:caa872941b876f7ad73abc60144f9a7f6efb575e4f91c4fc1517f0339bcea01e \ - --hash=sha256:da91da0d42e70cd8bda8e6687fab2afd28513a3cc9434539f4149610e63baf8f \ - --hash=sha256:e17b85919d461583aa7e0171bb4f419a6545b261ca080984db49b1f8dced1d4b \ - --hash=sha256:e2df41763d7a9fd438b6b7bde7b75eb3a92ef2f4682ed2d8e4b997b5f0c76ca9 \ - --hash=sha256:e76be5a98dc6c29d85dfa72eb419e8d9276ee96ccf5c33f2b6828001907dcb17 \ - --hash=sha256:f2e209a283c9fa423e268cad015ec4fb249178608f755fb67491ff175ecbffbf - # via -r requirements.in -types-requests==2.32.0.20250306 \ - --hash=sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1 \ - --hash=sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b - # via -r requirements.in -typing-extensions==4.12.2 \ - --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ - --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 - # via - # mypy - # pydantic - # pydantic-core -urllib3==2.3.0 \ - --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ - --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d - # via - # requests - # types-requests