-
Notifications
You must be signed in to change notification settings - Fork 375
Unexpected Behavior: MySqlContainer _connect implementation is flaky #707
Description
Describe the bug
The current implementation for MySqlContainer to check if the database is running is like below:
wait_for_logs( self, re.compile(".*: ready for connections.*: ready for connections.*", flags=re.DOTALL | re.MULTILINE).search, )
It expects for two pair of "ready for connections" logs to appear in the logs output. This works for vanilla MySQL Docker image but fails for any custom ones, for instance, an image that has a custom database initilization which is something standard and explained how in the official Docker Hub for MySQL section 'Initializing a fresh instance'.
Below is a example of a custom MySQL docker image:
FROM mysql:latest as builder
# Modify the entrypoint to only initialize the database
RUN sed -i 's/exec "\$@"/echo "not running \$@"/' /usr/local/bin/docker-entrypoint.sh
ENV MYSQL_ROOT_PASSWORD=test
# Copy data for initialization and settings
COPY sql/ /docker-entrypoint-initdb.d/
COPY ./bigtext.txt /var/lib/mysql-files/
COPY my.cnf /etc/mysql/my.cnf
# Run the entrypoint script to initialize the database
RUN /usr/local/bin/docker-entrypoint.sh mysqld
FROM mysql:latest
# Copying settings
COPY my.cnf /etc/mysql/my.cnf
# Copy the initialized database from the first stage
COPY --from=builder /var/lib/mysql /var/lib/mysql
# Copy the health table
COPY health-check.sql /health-check.sql
# Override the normal startup to load health-check after database initialization
ENTRYPOINT ["docker-entrypoint.sh", "mysqld", "--init-file", "/health-check.sql"]
And the initialization logs output for this image is like below:
docker run --rm -v ./tmp:/var/lib/mysql -eMYSQL_RANDOM_ROOT_PASSWORD=true sakila-extended-source-db:latest
2024年10月01日 17:06:45+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 9.0.1-1.el9 started.
2024年10月01日 17:06:46+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2024年10月01日 17:06:46+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 9.0.1-1.el9 started.
'/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
2024年10月01日T17:06:46.171337Z 0 [System] [MY-015015] [Server] MySQL Server - start.
2024年10月01日T17:06:46.312696Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 9.0.1) starting as process 1
2024年10月01日T17:06:46.314299Z 0 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
2024年10月01日T17:06:46.317485Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024年10月01日T17:06:46.396872Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024年10月01日T17:06:46.517414Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2024年10月01日T17:06:46.517454Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2024年10月01日T17:06:46.519802Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2024年10月01日T17:06:46.525651Z 7 [ERROR] [MY-000061] [Server] 1049 Unknown database 'sakila'.
2024年10月01日T17:06:46.526937Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2024年10月01日T17:06:46.526956Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '9.0.1' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
Logs from vanilla mysql:latest image:
docker run --rm -v ./tmp:/var/lib/mysql -eMYSQL_RANDOM_ROOT_PASSWORD=true mysql:latest core -> feature/monorepo-cleanup + ? !
2024年10月01日 17:04:05+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 9.0.1-1.el9 started.
2024年10月01日 17:04:05+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2024年10月01日 17:04:05+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 9.0.1-1.el9 started.
2024年10月01日 17:04:05+00:00 [Note] [Entrypoint]: Initializing database files
2024年10月01日T17:04:05.565798Z 0 [System] [MY-015017] [Server] MySQL Server Initialization - start.
2024年10月01日T17:04:05.566683Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 9.0.1) initializing of server in progress as process 81
2024年10月01日T17:04:05.568354Z 0 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
2024年10月01日T17:04:05.575007Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024年10月01日T17:04:05.689247Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024年10月01日T17:04:06.519129Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
2024年10月01日T17:04:08.317899Z 0 [System] [MY-015018] [Server] MySQL Server Initialization - end.
2024年10月01日 17:04:08+00:00 [Note] [Entrypoint]: Database files initialized
2024年10月01日 17:04:08+00:00 [Note] [Entrypoint]: Starting temporary server
2024年10月01日T17:04:08.364408Z 0 [System] [MY-015015] [Server] MySQL Server - start.
2024年10月01日T17:04:08.507157Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 9.0.1) starting as process 124
2024年10月01日T17:04:08.508364Z 0 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
2024年10月01日T17:04:08.515275Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024年10月01日T17:04:08.594411Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024年10月01日T17:04:08.718336Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2024年10月01日T17:04:08.718374Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2024年10月01日T17:04:08.720619Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2024年10月01日T17:04:08.729333Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
2024年10月01日T17:04:08.729354Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '9.0.1' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL.
2024年10月01日 17:04:08+00:00 [Note] [Entrypoint]: Temporary server started.
'/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leapseconds' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/tzdata.zi' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
2024年10月01日 17:04:09+00:00 [Note] [Entrypoint]: GENERATED ROOT PASSWORD: uLXlPdWc6VAglkpK99BoRXuNgH8c7z9s
2024年10月01日 17:04:09+00:00 [Note] [Entrypoint]: Stopping temporary server
2024年10月01日T17:04:09.444930Z 10 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 9.0.1).
2024年10月01日T17:04:10.277268Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 9.0.1) MySQL Community Server - GPL.
2024年10月01日T17:04:10.277290Z 0 [System] [MY-015016] [Server] MySQL Server - end.
2024年10月01日 17:04:10+00:00 [Note] [Entrypoint]: Temporary server stopped
2024年10月01日 17:04:10+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
2024年10月01日T17:04:10.475599Z 0 [System] [MY-015015] [Server] MySQL Server - start.
2024年10月01日T17:04:10.622838Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 9.0.1) starting as process 1
2024年10月01日T17:04:10.623975Z 0 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
2024年10月01日T17:04:10.626794Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024年10月01日T17:04:10.705627Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024年10月01日T17:04:10.831741Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2024年10月01日T17:04:10.831785Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2024年10月01日T17:04:10.834154Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2024年10月01日T17:04:10.842164Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2024年10月01日T17:04:10.842236Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '9.0.1' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
In the vanilla there are twice the pair "ready for connections" which makes the regular expression match, but it fails for the custom one. Of course, another approach would be to create a custom testcontainers docker image class but actually this isn't a good idea because the logs for MySql can change anytime and as soon as the official image gets updated testcontainers MySql image class may fail to properly check if the container has started or not.
To Reproduce
Create a docker image like the Dockerfile above and use it in test using testcontainers like below
>>> import sqlalchemy >>> from testcontainers.mysql import MySqlContainer >>> with MySqlContainer('mysql-custom:latest') as mysql: ... engine = sqlalchemy.create_engine(mysql.get_connection_url()) ... with engine.begin() as connection: ... result = connection.execute(sqlalchemy.text("select version()")) ... version, = result.fetchone()
The test will timeout on waiting for the container to start even that it has already started.
I monkey patched the implementation on my local project like below and worked well. The approach below is more reliable as it relies on standard Mysql tooling to check if the service is running or not, also it make use of testcontainers builtin constructs where both tends to be more future-proof.
File modules/mysql/testcontainers/mysql/__init__.py
... # this code overwrite the method `_connect` of class `MySqlContainer` specified in the file above. def _connect(self) -> None: def check_if_is_running(): escaped_single_password = self.password.replace("'", "'\"'\"'") result = self.exec( [ "sh", "-c", f"mysql -u {self.username} -p{escaped_single_password} -h {self.get_container_host_ip()} -P {self.port} -e 'SELECT VERSION();'" ] ) if result.exit_code != 0: raise ConnectionError(f"Failed to connect to MySQL: {result.output}") wait_for(check_if_is_running)
I have it running and tested on my project and I would be happy to submit a PR.
Runtime environment
Provide a summary of your runtime environment. Which operating system, python version, and docker version are you using? What is the version of testcontainers-python you are using? You can run the following commands to get the relevant information.
# Get the operating system information (on a unix os). $ uname -a Darwin ****** 24.1.0 Darwin Kernel Version 24.1.0: Tue Sep 17 07:49:16 PDT 2024; root:xnu-11215.40.59~38/RELEASE_ARM64_T6030 arm6 # Get the python version. $ python --version Python 3.11.9 # Get the docker version and other docker information. $ docker info Client: Version: 27.2.0 Context: desktop-linux Debug Mode: false Plugins: buildx: Docker Buildx (Docker Inc.) Version: v0.16.2-desktop.1 Path: /Users/*****/.docker/cli-plugins/docker-buildx compose: Docker Compose (Docker Inc.) Version: v2.29.2-desktop.2 Path: /Users/*****/.docker/cli-plugins/docker-compose debug: Get a shell into any image or container (Docker Inc.) Version: 0.0.34 Path: /Users/*****/.docker/cli-plugins/docker-debug desktop: Docker Desktop commands (Alpha) (Docker Inc.) Version: v0.0.15 Path: /Users/*****/.docker/cli-plugins/docker-desktop dev: Docker Dev Environments (Docker Inc.) Version: v0.1.2 Path: /Users/*****/.docker/cli-plugins/docker-dev extension: Manages Docker extensions (Docker Inc.) Version: v0.2.25 Path: /Users/*****/.docker/cli-plugins/docker-extension feedback: Provide feedback, right in your terminal! (Docker Inc.) Version: v1.0.5 Path: /Users/*****/.docker/cli-plugins/docker-feedback init: Creates Docker-related starter files for your project (Docker Inc.) Version: v1.3.0 Path: /Users/*****/.docker/cli-plugins/docker-init sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.) Version: 0.6.0 Path: /Users/*****/.docker/cli-plugins/docker-sbom scout: Docker Scout (Docker Inc.) Version: v1.13.0 Path: /Users/*****/.docker/cli-plugins/docker-scout Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 25 Server Version: 27.2.0 Storage Driver: overlayfs driver-type: io.containerd.snapshotter.v1 Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 2 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 runc Default Runtime: runc Init Binary: docker-init containerd version: 8fc6bcff51318944179630522a095cc9dbf9f353 runc version: v1.1.13-0-g58aa920 init version: de40ad0 Security Options: seccomp Profile: unconfined cgroupns Kernel Version: 6.10.4-linuxkit Operating System: Docker Desktop OSType: linux Architecture: aarch64 CPUs: 12 Total Memory: 17.3GiB Name: docker-desktop ID: 98c933e7-9d31-46e1-a51e-eda5f0f7dc99 Docker Root Dir: /var/lib/docker Debug Mode: false HTTP Proxy: http.docker.internal:3128 HTTPS Proxy: http.docker.internal:3128 No Proxy: hubproxy.docker.internal Labels: com.docker.desktop.address=unix:///Users/*****/Library/Containers/com.docker.docker/Data/docker-cli.sock Experimental: false Insecure Registries: hubproxy.docker.internal:5555 127.0.0.0/8 Live Restore Enabled: false # Get all python packages. $ pip freeze aiohappyeyeballs==2.4.0 aiohttp==3.10.5 aiohttp-cors==0.7.0 aiosignal==1.3.1 alabaster==0.7.16 annotated-types==0.7.0 anytree==2.12.1 appnope==0.1.4 argcomplete==3.5.0 astroid==2.15.8 asttokens==2.4.1 attrs==24.2.0 azure-core==1.30.2 azure-storage-blob==12.22.0 azure-storage-file-datalake==12.16.0 Babel==2.15.0 bandit==1.7.9 bcrypt==4.2.0 blis==0.7.11 boto3==1.34.78 botocore==1.34.156 cachetools==5.4.0 catalogue==2.0.10 certifi==2024年7月4日 cffi==1.17.0 charset-normalizer==3.3.2 click==8.1.7 cloudpathlib==0.18.1 colorful==0.5.6 comm==0.2.2 confection==0.1.5 contourpy==1.2.1 coverage==7.6.1 cryptography==42.0.8 cx_Oracle==8.3.0 cycler==0.12.1 cymem==2.0.8 databricks-sql-connector==3.3.0 de-core-news-md @ https://github.com/explosion/spacy-models/releases/download/de_core_news_md-3.7.0/de_core_news_md-3.7.0-py3-none-any.whl#sha256=1426896f135b7e6314637faa9025a3e580c9ba2785372ccffe723dc20b41c493 de-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/de_core_news_sm-3.7.0/de_core_news_sm-3.7.0-py3-none-any.whl#sha256=d88c737eb7eb766f730f6a2dcb99dfcdb81623e1e0d89a9c638a2182ac19c52e debugpy==1.8.5 decorator==5.1.1 deepdiff==6.7.1 defusedxml==0.7.1 dill==0.3.8 diskcache==5.6.3 distlib==0.3.8 docker==7.1.0 docutils==0.17.1 dtaidistance==2.3.12 ed25519==1.5 elastic-transport==8.13.1 elasticsearch==8.14.0 en-core-web-md @ https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.7.1/en_core_web_md-3.7.1-py3-none-any.whl#sha256=6a0f857a2b4d219c6fa17d455f82430b365bf53171a2d919b9376e5dc9be032e en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl#sha256=86cc141f63942d4b2c5fcee06630fd6f904788d2f0ab005cce45aadb8fb73889 et-xmlfile==1.1.0 executing==2.0.1 Faker==24.4.0 ff3==1.0.2 filelock==3.15.4 flake8==4.0.1 fonttools==4.53.1 frozendict==2.4.4 frozenlist==1.4.1 future==1.0.0 geohash2==1.1 geojson==3.1.0 google-api-core==2.19.2 google-auth==2.34.0 googleapis-common-protos==1.65.0 greenlet==3.0.3 grpcio==1.66.1 gssapi==1.8.3 hotxlfp==0.0.15 ibm-db-sa==0.4.0 ibm_db==3.2.3 idna==3.7 imagesize==1.4.1 importlib_resources==6.4.0 iniconfig==2.0.0 ipykernel==6.29.5 ipython==8.26.0 isodate==0.6.1 isort==5.13.2 ja-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/ja_core_news_sm-3.7.0/ja_core_news_sm-3.7.0-py3-none-any.whl#sha256=1191e5bbffcc90670146616c274a64850e54d12070bc5846e78a094f2f6fcfca jedi==0.19.1 Jinja2==3.1.4 jmespath==1.0.1 joblib==1.4.2 jsonschema==4.23.0 jsonschema-specifications==2023年12月1日 jupyter_client==8.6.2 jupyter_core==5.7.2 kiwisolver==1.4.5 langcodes==3.4.0 language_data==1.2.0 lazy-object-proxy==1.10.0 Levenshtein==0.25.1 libnfs @ git+https://github.com/sahlberg/libnfs-python.git@b3cab5a6c34dbafc5e58137d7f91f892c0d46dc5 lz4==4.3.3 marisa-trie==1.2.0 markdown-it-py==3.0.0 MarkupSafe==2.1.5 matplotlib==3.9.1.post1 matplotlib-inline==0.1.7 mccabe==0.6.1 mdurl==0.1.2 mimesis==15.1.0 moto==5.0.12 msgpack==1.0.8 multidict==6.1.0 murmurhash==1.0.10 mysql-connector-python==8.4.0 mysqlclient==2.2.4 nest-asyncio==1.6.0 networkx==3.3 nl-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/nl_core_news_lg-3.7.0/nl_core_news_lg-3.7.0-py3-none-any.whl#sha256=02194e98105584a6e5008a523a94a5d6e4da2feb0964741646c507c8e20730d4 nl-core-news-md @ https://github.com/explosion/spacy-models/releases/download/nl_core_news_md-3.7.0/nl_core_news_md-3.7.0-py3-none-any.whl#sha256=9b0bacc6d15202bbbd3a9585003d9edec44c6a1f1d819b51e8bc6a6d0b4458a8 nl-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/nl_core_news_sm-3.7.0/nl_core_news_sm-3.7.0-py3-none-any.whl#sha256=9255c3ce9e639f0a7aa8e898f4514e6ad803284858241385e045f99637753770 numpy==1.26.4 numpydoc==1.5.0 oauthlib==3.2.2 odbcinst==1.0.1 opencensus==0.11.4 opencensus-context==0.1.3 openpyxl==3.1.5 ordered-set==4.1.0 packaging==24.1 pandas==2.1.4 paramiko==3.4.0 parso==0.8.4 pbr==6.0.0 pexpect==4.9.0 phonenumbers==8.13.42 pillow==10.4.0 pipx==1.6.0 platformdirs==4.2.2 pluggy==1.5.0 ply==3.11 preshed==3.0.9 presidio-analyzer==2.2.353 presidio-anonymizer==2.2.353 prometheus_client==0.20.0 prompt_toolkit==3.0.47 proto-plus==1.24.0 protobuf==5.27.3 psutil==6.0.0 psycopg2-binary==2.9.9 ptyprocess==0.7.0 pure-sasl==0.6.2 pure_eval==0.2.3 py-cpuinfo==9.0.0 py-spy==0.3.14 pyarrow==14.0.2 pyasn1==0.6.1 pyasn1_modules==0.4.1 pycodestyle==2.8.0 pycparser==2.22 pycryptodome==3.20.0 pydantic==2.8.2 pydantic_core==2.20.1 pyfarmhash==0.3.2 pyflakes==2.4.0 Pygments==2.18.0 PyHive==0.7.0 pylint==2.17.7 Pympler==1.1 PyMySQL==1.1.1 PyNaCl==1.5.0 pyodbc==5.1.0 pyOpenSSL==24.2.1 pyparsing==3.1.2 PyRSMQ==0.5.1 pytest==7.4.4 pytest-azurepipelines==1.0.5 pytest-benchmark==4.0.0 pytest-cov==4.1.0 pytest-env==1.1.3 pytest-nunit==1.0.7 python-dateutil==2.9.0.post0 python-Levenshtein==0.25.1 pytz==2024.1 PyYAML==6.0.2 pyzmq==26.1.0 rapidfuzz==3.9.6 ray==2.10.0 redis==5.0.8 referencing==0.35.1 regex==2024年7月24日 requests==2.32.3 requests-file==2.1.0 responses==0.25.3 rich==13.7.1 rpds-py==0.20.0 rsa==4.9 ruff==0.5.6 s3transfer==0.10.2 scikit-learn==1.5.1 scipy==1.13.1 seaborn==0.13.2 shapely==2.0.5 shellingham==1.5.4 six==1.16.0 smart-open==7.0.4 snowballstemmer==2.2.0 spacy==3.7.5 spacy-legacy==3.0.12 spacy-loggers==1.0.5 Sphinx==4.5.0 sphinx-autoapi==1.9.0 sphinx-rtd-theme==1.3.0 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 sphinxcontrib-htmlhelp==2.1.0 sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 SQLAlchemy==2.0.32 srsly==2.4.8 sshtunnel==0.4.0 stack-data==0.6.3 stevedore==5.2.0 SudachiDict-core==20240716 SudachiPy==0.6.8 testcontainers==4.8.1 thinc==8.2.5 threadpoolctl==3.5.0 thrift==0.16.0 thrift-sasl==0.4.3 tldextract==5.1.2 tomlkit==0.13.0 tornado==6.4.1 tqdm==4.66.5 traitlets==5.14.3 typer==0.12.3 typing_extensions==4.12.2 tzdata==2024.1 Unidecode==1.3.8 urllib3==2.2.2 userpath==1.9.2 virtualenv==20.26.4 wasabi==1.1.3 wcwidth==0.2.13 weasel==0.4.1 Werkzeug==3.0.3 wrapt==1.16.0 xmltodict==0.13.0 yarl==1.11.1