From 4223fbce48dd636be947abcfb813f2c5125e9f0e Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 21:41:47 +0200 Subject: [PATCH 01/29] chore(main): migrate project to src layout --- .readthedocs.yml | 2 +- Makefile | 4 +- core/tests/test_project_settings.py | 37 ------ .../README.rst => docs/community/arangodb.rst | 0 .../aws/README.rst => docs/community/aws.rst | 0 .../README.rst => docs/community/azurite.rst | 0 .../community/cassandra.rst | 0 .../README.rst => docs/community/chroma.rst | 0 .../community/clickhouse.rst | 0 .../community/cockroachdb.rst | 0 .../README.rst => docs/community/cosmosdb.rst | 0 .../db2/README.rst => docs/community/db2.rst | 0 .../community/elasticsearch.rst | 0 .../README.rst => docs/community/generic.rst | 0 .../README.rst => docs/community/google.rst | 0 .../README.rst => docs/community/influxdb.rst | 0 .../k3s/README.rst => docs/community/k3s.rst | 0 .../README.rst => docs/community/kafka.rst | 0 .../README.rst => docs/community/keycloak.rst | 0 .../community/localstack.rst | 0 .../README.rst => docs/community/mailpit.rst | 0 .../community/memcached.rst | 0 .../README.rst => docs/community/milvus.rst | 0 .../README.rst => docs/community/minio.rst | 0 .../README.rst => docs/community/mongodb.rst | 0 .../README.rst => docs/community/mqtt.rst | 0 .../README.rst => docs/community/mssql.rst | 0 .../README.rst => docs/community/mysql.rst | 0 .../README.rst => docs/community/nats.rst | 0 .../README.rst => docs/community/neo4j.rst | 0 .../README.rst => docs/community/nginx.rst | 0 .../README.rst => docs/community/ollama.rst | 0 .../README.rst => docs/community/openfga.rst | 0 .../community/opensearch.rst | 0 .../community/oracle-free.rst | 0 .../README.rst => docs/community/postgres.rst | 0 .../README.rst => docs/community/qdrant.rst | 0 .../README.rst => docs/community/rabbitmq.rst | 0 .../README.rst => docs/community/redis.rst | 0 .../README.rst => docs/community/registry.rst | 0 .../README.rst => docs/community/scylla.rst | 0 .../README.rst => docs/community/selenium.rst | 0 .../README.rst => docs/community/sftp.rst | 0 .../README.rst => docs/community/trino.rst | 0 .../README.rst => docs/community/valkey.rst | 0 .../README.rst => docs/community/vault.rst | 0 .../README.rst => docs/community/weaviate.rst | 0 compose.rst => docs/compose.rst | 0 conf.py => docs/conf.py | 0 {core => docs/core}/README.rst | 0 index.rst => docs/index.rst | 2 +- docs/modules/arangodb.md | 2 +- .../modules/arangodb_example.py | 0 docs/modules/aws.md | 2 +- .../modules/aws_example.py | 0 docs/modules/azurite.md | 2 +- .../modules/azurite_example.py | 0 docs/modules/cassandra.md | 2 +- .../modules/cassandra_example.py | 0 docs/modules/chroma.md | 2 +- .../modules/chroma_example.py | 0 docs/modules/clickhouse.md | 2 +- .../modules/clickhouse_example.py | 0 docs/modules/cockroachdb.md | 2 +- .../modules/cockroachdb_example.py | 0 docs/modules/cosmosdb.md | 2 +- .../modules/cosmosdb_example.py | 0 docs/modules/db2.md | 2 +- .../modules/db2_example.py | 0 docs/modules/elasticsearch.md | 2 +- .../modules/elasticsearch_example.py | 0 docs/modules/generic.md | 2 +- .../modules/generic_example.py | 0 docs/modules/google.md | 2 +- .../modules/google_example.py | 0 docs/modules/influxdb.md | 2 +- .../modules/influxdb_example.py | 0 docs/modules/k3s.md | 2 +- .../modules/k3s_example.py | 0 docs/modules/kafka.md | 2 +- .../modules/kafka_example.py | 0 docs/modules/keycloak.md | 2 +- .../modules/keycloak_example.py | 0 docs/modules/localstack.md | 2 +- .../modules/localstack_example.py | 0 docs/modules/mailpit.md | 2 +- .../modules/mailpit_example.py | 0 docs/modules/memcached.md | 2 +- .../modules/memcached_example.py | 0 docs/modules/milvus.md | 2 +- .../modules/milvus_example.py | 0 docs/modules/minio.md | 2 +- .../modules/minio_example.py | 0 docs/modules/mongodb.md | 2 +- .../modules/mongodb_example.py | 0 docs/modules/mqtt.md | 2 +- .../modules/mqtt_example.py | 0 docs/modules/mssql.md | 2 +- .../modules/mssql_example.py | 0 docs/modules/mysql.md | 2 +- .../modules/mysql_example.py | 0 docs/modules/nats.md | 2 +- .../modules/nats_example.py | 0 docs/modules/neo4j.md | 2 +- .../modules/neo4j_example.py | 0 docs/modules/nginx.md | 2 +- .../modules/nginx_example.py | 0 docs/modules/ollama.md | 2 +- .../modules/ollama_example.py | 0 docs/modules/opensearch.md | 2 +- .../modules/opensearch_example.py | 0 docs/modules/oracle-free.md | 2 +- .../modules/oracle-free_example.py | 0 docs/modules/postgres.md | 2 +- .../modules/postgres_example.py | 0 docs/modules/qdrant.md | 2 +- .../modules/qdrant_example.py | 0 docs/modules/rabbitmq.md | 2 +- .../modules/rabbitmq_example.py | 0 docs/modules/redis.md | 2 +- .../modules/redis_example.py | 0 docs/modules/registry.md | 2 +- .../modules/registry_example.py | 0 docs/modules/scylla.md | 2 +- .../modules/scylla_example.py | 0 docs/modules/selenium.md | 2 +- .../modules/selenium_example.py | 0 docs/modules/sftp.md | 2 +- .../modules/sftp_example.py | 0 docs/modules/test_module_import.md | 100 ---------------- docs/modules/trino.md | 2 +- .../modules/trino_example.py | 0 docs/modules/valkey.md | 2 +- .../modules/valkey_example.py | 0 docs/modules/vault.md | 2 +- .../modules/vault_example.py | 0 docs/modules/weaviate.md | 2 +- .../modules/weaviate_example.py | 0 modules/test_module_import/README.rst | 2 - .../examples/01_basic_import.py | 58 --------- .../examples/02_module_reloading.py | 41 ------- .../examples/03_version_specific.py | 34 ------ .../examples/04_dependencies_and_env.py | 48 -------- .../examples/05_advanced_features.py | 59 ---------- .../test_module_import/__init__.py | 1 - .../test_module_import/new_sub_module.py | 27 ----- .../test_module_import/tests/test_mock_one.py | 14 --- pyproject.toml | 111 ++---------------- .../app => src/testcontainers}/__init__.py | 0 .../community}/arangodb/__init__.py | 0 .../testcontainers/community}/aws/__init__.py | 0 .../community}/aws/aws_lambda.py | 0 .../community}/azurite/__init__.py | 0 .../community/azurite}/py.typed | 0 .../community}/cassandra/__init__.py | 0 .../community}/chroma/__init__.py | 0 .../community}/clickhouse/__init__.py | 0 .../community}/cockroachdb/__init__.py | 0 .../community}/cosmosdb/__init__.py | 0 .../community}/cosmosdb/_emulator.py | 0 .../community}/cosmosdb/_grab.py | 0 .../community}/cosmosdb/mongodb.py | 0 .../community}/cosmosdb/nosql.py | 0 .../testcontainers/community}/db2/__init__.py | 0 .../community}/elasticsearch/__init__.py | 0 .../community}/generic/__init__.py | 0 .../community}/generic/providers/__init__.py | 0 .../providers/sql_connection_wait_strategy.py | 0 .../community}/generic/server.py | 1 - .../testcontainers/community}/generic/sql.py | 0 .../community}/google/__init__.py | 0 .../community}/google/datastore.py | 0 .../community}/google/pubsub.py | 0 .../community/influxdb/__init__.py | 0 .../community}/influxdb1/__init__.py | 0 .../community}/influxdb2/__init__.py | 0 .../testcontainers/community}/k3s/__init__.py | 0 .../community}/kafka/__init__.py | 0 .../community}/kafka/_redpanda.py | 0 .../community}/keycloak/__init__.py | 0 .../community}/localstack/__init__.py | 0 .../community}/mailpit/__init__.py | 0 .../community/mailpit}/py.typed | 0 .../community}/memcached/__init__.py | 0 .../community}/milvus/__init__.py | 0 .../community}/minio/__init__.py | 0 .../community}/mongodb/__init__.py | 0 .../community}/mqtt/__init__.py | 0 ...iners-mosquitto-default-configuration.conf | 0 .../community}/mssql/__init__.py | 0 .../community}/mysql/__init__.py | 0 .../community}/nats/__init__.py | 0 .../community}/neo4j/__init__.py | 0 .../community}/nginx/__init__.py | 0 .../community}/ollama/__init__.py | 0 .../community}/openfga/__init__.py | 0 .../community}/opensearch/__init__.py | 0 .../community}/oracle/__init__.py | 0 .../community}/postgres/__init__.py | 0 .../community/postgres}/py.typed | 0 .../community}/qdrant/__init__.py | 0 .../community}/rabbitmq/__init__.py | 0 .../community}/redis/__init__.py | 0 .../community}/registry/__init__.py | 0 .../community}/scylla/__init__.py | 0 .../community}/selenium/__init__.py | 0 .../community}/selenium/video.py | 0 .../community}/sftp/__init__.py | 0 .../testcontainers/community/sftp/py.typed | 0 .../community}/trino/__init__.py | 0 .../community}/valkey/__init__.py | 0 .../community}/vault/__init__.py | 0 .../community}/weaviate/__init__.py | 0 .../testcontainers/compose/__init__.py | 0 .../testcontainers/compose/compose.py | 0 {core => src}/testcontainers/core/__init__.py | 0 {core => src}/testcontainers/core/auth.py | 0 {core => src}/testcontainers/core/config.py | 0 .../testcontainers/core/container.py | 0 .../testcontainers/core/docker_client.py | 0 .../testcontainers/core/exceptions.py | 0 {core => src}/testcontainers/core/generic.py | 0 {core => src}/testcontainers/core/image.py | 0 {core => src}/testcontainers/core/inspect.py | 0 {core => src}/testcontainers/core/labels.py | 0 {core => src}/testcontainers/core/network.py | 0 .../testcontainers/core/transferable.py | 0 {core => src}/testcontainers/core/utils.py | 0 {core => src}/testcontainers/core/version.py | 0 .../testcontainers/core/wait_strategies.py | 0 .../testcontainers/core/waiting_utils.py | 0 .../testcontainers/socat/__init__.py | 0 {core => src}/testcontainers/socat/socat.py | 0 .../tests => tests/community}/__init__.py | 0 .../community/arangodb}/test_arangodb.py | 0 .../community/aws}/lambda_sample/Dockerfile | 0 .../aws}/lambda_sample/lambda_function.py | 0 .../tests => tests/community/aws}/test_aws.py | 0 .../samples/network_container/Dockerfile | 0 .../network_container/netowrk_container.py | 0 .../community/azurite}/test_azurite.py | 0 .../community/cassandra}/test_cassandra.py | 0 .../community/chroma}/test_chroma.py | 0 .../community/clickhouse}/test_clickhouse.py | 0 .../cockroachdb}/test_cockroachdb.py | 0 .../cosmosdb}/test_cosmosdb_emulator.py | 0 .../cosmosdb}/test_cosmosdb_mongodb.py | 0 .../cosmosdb}/test_cosmosdb_nosql.py | 0 .../tests => tests/community/db2}/test_db2.py | 0 .../elasticsearch}/test_elasticsearch.py | 0 .../community/generic}/conftest.py | 0 .../generic}/samples/advance_1/Dockerfile | 0 .../generic/samples/advance_1/app/__init__.py | 0 .../generic}/samples/advance_1/app/main.py | 0 .../generic}/samples/fastapi/Dockerfile | 0 .../generic/samples/fastapi/app/__init__.py | 0 .../generic}/samples/fastapi/app/main.py | 0 .../generic}/samples/python_server/Dockerfile | 0 .../community/generic}/test_server.py | 0 .../community/generic}/test_sql.py | 0 .../community/google}/test_google.py | 0 tests/community/influxdb/__init__.py | 0 .../community/influxdb}/test_influxdb.py | 0 .../tests => tests/community/k3s}/test_k3s.py | 0 .../community/kafka}/test_kafka.py | 0 .../community/kafka}/test_redpanda.py | 0 .../community/keycloak}/test_keycloak.py | 0 .../community/localstack}/test_localstack.py | 0 .../community/mailpit}/test_mailpit.py | 0 .../community/memcached}/test_memcached.py | 0 .../community/milvus}/test_milvus.py | 0 .../community/minio}/test_minio.py | 0 .../community/mongodb}/test_mongodb.py | 0 .../community/mqtt}/test_mosquitto.py | 0 .../community/mssql}/test_mssql.py | 0 .../community/mysql}/seeds/01-schema.sql | 0 .../community/mysql}/seeds/02-seeds.sql | 0 .../community/mysql}/test_mysql.py | 0 .../community/nats}/test_nats.py | 0 .../community/nats}/test_nats_jetstream.py | 0 .../community/neo4j}/test_neo4j.py | 0 .../community/nginx}/test_nginx.py | 0 .../community/ollama}/test_ollama.py | 0 .../community/openfga}/test_openfga.py | 0 .../community/opensearch}/test_opensearch.py | 0 .../community/oracle-free}/test_oracle.py | 0 .../postgres_create_example_table.sql | 0 .../community/postgres}/test_postgres.py | 0 .../community/qdrant}/test_config.yaml | 0 .../community/qdrant}/test_qdrant.py | 0 .../community/rabbitmq}/test_rabbitmq.py | 0 .../community/redis}/test_redis.py | 0 .../community/registry}/test_registry.py | 0 .../community/scylla}/test_scylla.py | 0 .../community/selenium}/test_selenium.py | 0 .../community/sftp}/test_sftp.py | 0 .../community/trino}/test_trino.py | 0 .../community/valkey}/test_valkey.py | 0 .../community/vault}/test_vault.py | 0 .../community/weaviate}/test_weaviate.py | 0 tests/core/__init__.py | 0 .../core}/_local_registry_container.py | 0 .../basic/docker-compose.yaml | 0 .../core}/compose_fixtures/basic/hello.yaml | 0 .../basic_multiple/docker-compose.yaml | 0 .../basic_volume/docker-compose.yaml | 0 .../port_multiple/compose.yaml | 0 .../compose_fixtures/port_single/compose.yaml | 0 .../profile_support/compose.yaml | 0 {core/tests => tests/core}/conftest.py | 0 .../core}/image_fixtures/busybox/Dockerfile | 0 .../core}/image_fixtures/sample/Dockerfile | 0 {core/tests => tests/core}/test_auth.py | 0 {core/tests => tests/core}/test_compose.py | 0 {core/tests => tests/core}/test_config.py | 0 {core/tests => tests/core}/test_container.py | 0 {core/tests => tests/core}/test_core.py | 0 {core/tests => tests/core}/test_core_ports.py | 0 .../core}/test_core_registry.py | 0 .../core}/test_docker_client.py | 0 .../core}/test_docker_in_docker.py | 0 {core/tests => tests/core}/test_image.py | 0 {core/tests => tests/core}/test_inspect.py | 0 {core/tests => tests/core}/test_labels.py | 0 {core/tests => tests/core}/test_network.py | 0 .../core}/test_new_docker_api.py | 0 .../core}/test_protocol_compliance.py | 0 {core/tests => tests/core}/test_ryuk.py | 0 {core/tests => tests/core}/test_socat.py | 0 .../tests => tests/core}/test_transferable.py | 0 {core/tests => tests/core}/test_utils.py | 0 {core/tests => tests/core}/test_version.py | 0 .../core}/test_wait_strategies.py | 0 .../core}/test_wait_strategies_integration.py | 0 .../core}/test_waiting_utils.py | 0 335 files changed, 57 insertions(+), 570 deletions(-) delete mode 100644 core/tests/test_project_settings.py rename modules/arangodb/README.rst => docs/community/arangodb.rst (100%) rename modules/aws/README.rst => docs/community/aws.rst (100%) rename modules/azurite/README.rst => docs/community/azurite.rst (100%) rename modules/cassandra/README.rst => docs/community/cassandra.rst (100%) rename modules/chroma/README.rst => docs/community/chroma.rst (100%) rename modules/clickhouse/README.rst => docs/community/clickhouse.rst (100%) rename modules/cockroachdb/README.rst => docs/community/cockroachdb.rst (100%) rename modules/cosmosdb/README.rst => docs/community/cosmosdb.rst (100%) rename modules/db2/README.rst => docs/community/db2.rst (100%) rename modules/elasticsearch/README.rst => docs/community/elasticsearch.rst (100%) rename modules/generic/README.rst => docs/community/generic.rst (100%) rename modules/google/README.rst => docs/community/google.rst (100%) rename modules/influxdb/README.rst => docs/community/influxdb.rst (100%) rename modules/k3s/README.rst => docs/community/k3s.rst (100%) rename modules/kafka/README.rst => docs/community/kafka.rst (100%) rename modules/keycloak/README.rst => docs/community/keycloak.rst (100%) rename modules/localstack/README.rst => docs/community/localstack.rst (100%) rename modules/mailpit/README.rst => docs/community/mailpit.rst (100%) rename modules/memcached/README.rst => docs/community/memcached.rst (100%) rename modules/milvus/README.rst => docs/community/milvus.rst (100%) rename modules/minio/README.rst => docs/community/minio.rst (100%) rename modules/mongodb/README.rst => docs/community/mongodb.rst (100%) rename modules/mqtt/README.rst => docs/community/mqtt.rst (100%) rename modules/mssql/README.rst => docs/community/mssql.rst (100%) rename modules/mysql/README.rst => docs/community/mysql.rst (100%) rename modules/nats/README.rst => docs/community/nats.rst (100%) rename modules/neo4j/README.rst => docs/community/neo4j.rst (100%) rename modules/nginx/README.rst => docs/community/nginx.rst (100%) rename modules/ollama/README.rst => docs/community/ollama.rst (100%) rename modules/openfga/README.rst => docs/community/openfga.rst (100%) rename modules/opensearch/README.rst => docs/community/opensearch.rst (100%) rename modules/oracle-free/README.rst => docs/community/oracle-free.rst (100%) rename modules/postgres/README.rst => docs/community/postgres.rst (100%) rename modules/qdrant/README.rst => docs/community/qdrant.rst (100%) rename modules/rabbitmq/README.rst => docs/community/rabbitmq.rst (100%) rename modules/redis/README.rst => docs/community/redis.rst (100%) rename modules/registry/README.rst => docs/community/registry.rst (100%) rename modules/scylla/README.rst => docs/community/scylla.rst (100%) rename modules/selenium/README.rst => docs/community/selenium.rst (100%) rename modules/sftp/README.rst => docs/community/sftp.rst (100%) rename modules/trino/README.rst => docs/community/trino.rst (100%) rename modules/valkey/README.rst => docs/community/valkey.rst (100%) rename modules/vault/README.rst => docs/community/vault.rst (100%) rename modules/weaviate/README.rst => docs/community/weaviate.rst (100%) rename compose.rst => docs/compose.rst (100%) rename conf.py => docs/conf.py (100%) rename {core => docs/core}/README.rst (100%) rename index.rst => docs/index.rst (99%) rename modules/arangodb/example_basic.py => docs/modules/arangodb_example.py (100%) rename modules/aws/example_basic.py => docs/modules/aws_example.py (100%) rename modules/azurite/example_basic.py => docs/modules/azurite_example.py (100%) rename modules/cassandra/example_basic.py => docs/modules/cassandra_example.py (100%) rename modules/chroma/example_basic.py => docs/modules/chroma_example.py (100%) rename modules/clickhouse/example_basic.py => docs/modules/clickhouse_example.py (100%) rename modules/cockroachdb/example_basic.py => docs/modules/cockroachdb_example.py (100%) rename modules/cosmosdb/example_basic.py => docs/modules/cosmosdb_example.py (100%) rename modules/db2/example_basic.py => docs/modules/db2_example.py (100%) rename modules/elasticsearch/example_basic.py => docs/modules/elasticsearch_example.py (100%) rename modules/generic/example_basic.py => docs/modules/generic_example.py (100%) rename modules/google/example_basic.py => docs/modules/google_example.py (100%) rename modules/influxdb/example_basic.py => docs/modules/influxdb_example.py (100%) rename modules/k3s/example_basic.py => docs/modules/k3s_example.py (100%) rename modules/kafka/example_basic.py => docs/modules/kafka_example.py (100%) rename modules/keycloak/example_basic.py => docs/modules/keycloak_example.py (100%) rename modules/localstack/example_basic.py => docs/modules/localstack_example.py (100%) rename modules/mailpit/example_basic.py => docs/modules/mailpit_example.py (100%) rename modules/memcached/example_basic.py => docs/modules/memcached_example.py (100%) rename modules/milvus/example_basic.py => docs/modules/milvus_example.py (100%) rename modules/minio/example_basic.py => docs/modules/minio_example.py (100%) rename modules/mongodb/example_basic.py => docs/modules/mongodb_example.py (100%) rename modules/mqtt/example_basic.py => docs/modules/mqtt_example.py (100%) rename modules/mssql/example_basic.py => docs/modules/mssql_example.py (100%) rename modules/mysql/example_basic.py => docs/modules/mysql_example.py (100%) rename modules/nats/example_basic.py => docs/modules/nats_example.py (100%) rename modules/neo4j/example_basic.py => docs/modules/neo4j_example.py (100%) rename modules/nginx/example_basic.py => docs/modules/nginx_example.py (100%) rename modules/ollama/example_basic.py => docs/modules/ollama_example.py (100%) rename modules/azurite/testcontainers/azurite/py.typed => docs/modules/opensearch_example.py (100%) rename modules/oracle-free/example_basic.py => docs/modules/oracle-free_example.py (100%) rename modules/postgres/example_basic.py => docs/modules/postgres_example.py (100%) rename modules/qdrant/example_basic.py => docs/modules/qdrant_example.py (100%) rename modules/rabbitmq/example_basic.py => docs/modules/rabbitmq_example.py (100%) rename modules/redis/example_basic.py => docs/modules/redis_example.py (100%) rename modules/registry/example_basic.py => docs/modules/registry_example.py (100%) rename modules/scylla/example_basic.py => docs/modules/scylla_example.py (100%) rename modules/selenium/example_basic.py => docs/modules/selenium_example.py (100%) rename modules/sftp/example_basic.py => docs/modules/sftp_example.py (100%) delete mode 100644 docs/modules/test_module_import.md rename modules/trino/example_basic.py => docs/modules/trino_example.py (100%) rename modules/valkey/example_basic.py => docs/modules/valkey_example.py (100%) rename modules/vault/example_basic.py => docs/modules/vault_example.py (100%) rename modules/weaviate/example_basic.py => docs/modules/weaviate_example.py (100%) delete mode 100644 modules/test_module_import/README.rst delete mode 100644 modules/test_module_import/examples/01_basic_import.py delete mode 100644 modules/test_module_import/examples/02_module_reloading.py delete mode 100644 modules/test_module_import/examples/03_version_specific.py delete mode 100644 modules/test_module_import/examples/04_dependencies_and_env.py delete mode 100644 modules/test_module_import/examples/05_advanced_features.py delete mode 100644 modules/test_module_import/testcontainers/test_module_import/__init__.py delete mode 100644 modules/test_module_import/testcontainers/test_module_import/new_sub_module.py delete mode 100644 modules/test_module_import/tests/test_mock_one.py rename {modules/generic/tests/samples/advance_1/app => src/testcontainers}/__init__.py (100%) rename {modules/arangodb/testcontainers => src/testcontainers/community}/arangodb/__init__.py (100%) rename {modules/aws/testcontainers => src/testcontainers/community}/aws/__init__.py (100%) rename {modules/aws/testcontainers => src/testcontainers/community}/aws/aws_lambda.py (100%) rename {modules/azurite/testcontainers => src/testcontainers/community}/azurite/__init__.py (100%) rename {modules/mailpit/testcontainers/mailpit => src/testcontainers/community/azurite}/py.typed (100%) rename {modules/cassandra/testcontainers => src/testcontainers/community}/cassandra/__init__.py (100%) rename {modules/chroma/testcontainers => src/testcontainers/community}/chroma/__init__.py (100%) rename {modules/clickhouse/testcontainers => src/testcontainers/community}/clickhouse/__init__.py (100%) rename {modules/cockroachdb/testcontainers => src/testcontainers/community}/cockroachdb/__init__.py (100%) rename {modules/cosmosdb/testcontainers => src/testcontainers/community}/cosmosdb/__init__.py (100%) rename {modules/cosmosdb/testcontainers => src/testcontainers/community}/cosmosdb/_emulator.py (100%) rename {modules/cosmosdb/testcontainers => src/testcontainers/community}/cosmosdb/_grab.py (100%) rename {modules/cosmosdb/testcontainers => src/testcontainers/community}/cosmosdb/mongodb.py (100%) rename {modules/cosmosdb/testcontainers => src/testcontainers/community}/cosmosdb/nosql.py (100%) rename {modules/db2/testcontainers => src/testcontainers/community}/db2/__init__.py (100%) rename {modules/elasticsearch/testcontainers => src/testcontainers/community}/elasticsearch/__init__.py (100%) rename {modules/generic/testcontainers => src/testcontainers/community}/generic/__init__.py (100%) rename {modules/generic/testcontainers => src/testcontainers/community}/generic/providers/__init__.py (100%) rename {modules/generic/testcontainers => src/testcontainers/community}/generic/providers/sql_connection_wait_strategy.py (100%) rename {modules/generic/testcontainers => src/testcontainers/community}/generic/server.py (99%) rename {modules/generic/testcontainers => src/testcontainers/community}/generic/sql.py (100%) rename {modules/google/testcontainers => src/testcontainers/community}/google/__init__.py (100%) rename {modules/google/testcontainers => src/testcontainers/community}/google/datastore.py (100%) rename {modules/google/testcontainers => src/testcontainers/community}/google/pubsub.py (100%) rename modules/influxdb/testcontainers/influxdb.py => src/testcontainers/community/influxdb/__init__.py (100%) rename {modules/influxdb/testcontainers => src/testcontainers/community}/influxdb1/__init__.py (100%) rename {modules/influxdb/testcontainers => src/testcontainers/community}/influxdb2/__init__.py (100%) rename {modules/k3s/testcontainers => src/testcontainers/community}/k3s/__init__.py (100%) rename {modules/kafka/testcontainers => src/testcontainers/community}/kafka/__init__.py (100%) rename {modules/kafka/testcontainers => src/testcontainers/community}/kafka/_redpanda.py (100%) rename {modules/keycloak/testcontainers => src/testcontainers/community}/keycloak/__init__.py (100%) rename {modules/localstack/testcontainers => src/testcontainers/community}/localstack/__init__.py (100%) rename {modules/mailpit/testcontainers => src/testcontainers/community}/mailpit/__init__.py (100%) rename {modules/postgres/testcontainers/postgres => src/testcontainers/community/mailpit}/py.typed (100%) rename {modules/memcached/testcontainers => src/testcontainers/community}/memcached/__init__.py (100%) rename {modules/milvus/testcontainers => src/testcontainers/community}/milvus/__init__.py (100%) rename {modules/minio/testcontainers => src/testcontainers/community}/minio/__init__.py (100%) rename {modules/mongodb/testcontainers => src/testcontainers/community}/mongodb/__init__.py (100%) rename {modules/mqtt/testcontainers => src/testcontainers/community}/mqtt/__init__.py (100%) rename {modules/mqtt/testcontainers => src/testcontainers/community}/mqtt/testcontainers-mosquitto-default-configuration.conf (100%) rename {modules/mssql/testcontainers => src/testcontainers/community}/mssql/__init__.py (100%) rename {modules/mysql/testcontainers => src/testcontainers/community}/mysql/__init__.py (100%) rename {modules/nats/testcontainers => src/testcontainers/community}/nats/__init__.py (100%) rename {modules/neo4j/testcontainers => src/testcontainers/community}/neo4j/__init__.py (100%) rename {modules/nginx/testcontainers => src/testcontainers/community}/nginx/__init__.py (100%) rename {modules/ollama/testcontainers => src/testcontainers/community}/ollama/__init__.py (100%) rename {modules/openfga/testcontainers => src/testcontainers/community}/openfga/__init__.py (100%) rename {modules/opensearch/testcontainers => src/testcontainers/community}/opensearch/__init__.py (100%) rename {modules/oracle-free/testcontainers => src/testcontainers/community}/oracle/__init__.py (100%) rename {modules/postgres/testcontainers => src/testcontainers/community}/postgres/__init__.py (100%) rename {modules/sftp/testcontainers/sftp => src/testcontainers/community/postgres}/py.typed (100%) rename {modules/qdrant/testcontainers => src/testcontainers/community}/qdrant/__init__.py (100%) rename {modules/rabbitmq/testcontainers => src/testcontainers/community}/rabbitmq/__init__.py (100%) rename {modules/redis/testcontainers => src/testcontainers/community}/redis/__init__.py (100%) rename {modules/registry/testcontainers => src/testcontainers/community}/registry/__init__.py (100%) rename {modules/scylla/testcontainers => src/testcontainers/community}/scylla/__init__.py (100%) rename {modules/selenium/testcontainers => src/testcontainers/community}/selenium/__init__.py (100%) rename {modules/selenium/testcontainers => src/testcontainers/community}/selenium/video.py (100%) rename {modules/sftp/testcontainers => src/testcontainers/community}/sftp/__init__.py (100%) rename modules/generic/tests/samples/fastapi/app/__init__.py => src/testcontainers/community/sftp/py.typed (100%) rename {modules/trino/testcontainers => src/testcontainers/community}/trino/__init__.py (100%) rename {modules/valkey/testcontainers => src/testcontainers/community}/valkey/__init__.py (100%) rename {modules/vault/testcontainers => src/testcontainers/community}/vault/__init__.py (100%) rename {modules/weaviate/testcontainers => src/testcontainers/community}/weaviate/__init__.py (100%) rename {core => src}/testcontainers/compose/__init__.py (100%) rename {core => src}/testcontainers/compose/compose.py (100%) rename {core => src}/testcontainers/core/__init__.py (100%) rename {core => src}/testcontainers/core/auth.py (100%) rename {core => src}/testcontainers/core/config.py (100%) rename {core => src}/testcontainers/core/container.py (100%) rename {core => src}/testcontainers/core/docker_client.py (100%) rename {core => src}/testcontainers/core/exceptions.py (100%) rename {core => src}/testcontainers/core/generic.py (100%) rename {core => src}/testcontainers/core/image.py (100%) rename {core => src}/testcontainers/core/inspect.py (100%) rename {core => src}/testcontainers/core/labels.py (100%) rename {core => src}/testcontainers/core/network.py (100%) rename {core => src}/testcontainers/core/transferable.py (100%) rename {core => src}/testcontainers/core/utils.py (100%) rename {core => src}/testcontainers/core/version.py (100%) rename {core => src}/testcontainers/core/wait_strategies.py (100%) rename {core => src}/testcontainers/core/waiting_utils.py (100%) rename {core => src}/testcontainers/socat/__init__.py (100%) rename {core => src}/testcontainers/socat/socat.py (100%) rename {modules/influxdb/tests => tests/community}/__init__.py (100%) rename {modules/arangodb/tests => tests/community/arangodb}/test_arangodb.py (100%) rename {modules/aws/tests => tests/community/aws}/lambda_sample/Dockerfile (100%) rename {modules/aws/tests => tests/community/aws}/lambda_sample/lambda_function.py (100%) rename {modules/aws/tests => tests/community/aws}/test_aws.py (100%) rename {modules/azurite/tests => tests/community/azurite}/samples/network_container/Dockerfile (100%) rename {modules/azurite/tests => tests/community/azurite}/samples/network_container/netowrk_container.py (100%) rename {modules/azurite/tests => tests/community/azurite}/test_azurite.py (100%) rename {modules/cassandra/tests => tests/community/cassandra}/test_cassandra.py (100%) rename {modules/chroma/tests => tests/community/chroma}/test_chroma.py (100%) rename {modules/clickhouse/tests => tests/community/clickhouse}/test_clickhouse.py (100%) rename {modules/cockroachdb/tests => tests/community/cockroachdb}/test_cockroachdb.py (100%) rename {modules/cosmosdb/tests => tests/community/cosmosdb}/test_cosmosdb_emulator.py (100%) rename {modules/cosmosdb/tests => tests/community/cosmosdb}/test_cosmosdb_mongodb.py (100%) rename {modules/cosmosdb/tests => tests/community/cosmosdb}/test_cosmosdb_nosql.py (100%) rename {modules/db2/tests => tests/community/db2}/test_db2.py (100%) rename {modules/elasticsearch/tests => tests/community/elasticsearch}/test_elasticsearch.py (100%) rename {modules/generic/tests => tests/community/generic}/conftest.py (100%) rename {modules/generic/tests => tests/community/generic}/samples/advance_1/Dockerfile (100%) rename modules/opensearch/example_basic.py => tests/community/generic/samples/advance_1/app/__init__.py (100%) rename {modules/generic/tests => tests/community/generic}/samples/advance_1/app/main.py (100%) rename {modules/generic/tests => tests/community/generic}/samples/fastapi/Dockerfile (100%) create mode 100644 tests/community/generic/samples/fastapi/app/__init__.py rename {modules/generic/tests => tests/community/generic}/samples/fastapi/app/main.py (100%) rename {modules/generic/tests => tests/community/generic}/samples/python_server/Dockerfile (100%) rename {modules/generic/tests => tests/community/generic}/test_server.py (100%) rename {modules/generic/tests => tests/community/generic}/test_sql.py (100%) rename {modules/google/tests => tests/community/google}/test_google.py (100%) create mode 100644 tests/community/influxdb/__init__.py rename {modules/influxdb/tests => tests/community/influxdb}/test_influxdb.py (100%) rename {modules/k3s/tests => tests/community/k3s}/test_k3s.py (100%) rename {modules/kafka/tests => tests/community/kafka}/test_kafka.py (100%) rename {modules/kafka/tests => tests/community/kafka}/test_redpanda.py (100%) rename {modules/keycloak/tests => tests/community/keycloak}/test_keycloak.py (100%) rename {modules/localstack/tests => tests/community/localstack}/test_localstack.py (100%) rename {modules/mailpit/tests => tests/community/mailpit}/test_mailpit.py (100%) rename {modules/memcached/tests => tests/community/memcached}/test_memcached.py (100%) rename {modules/milvus/tests => tests/community/milvus}/test_milvus.py (100%) rename {modules/minio/tests => tests/community/minio}/test_minio.py (100%) rename {modules/mongodb/tests => tests/community/mongodb}/test_mongodb.py (100%) rename {modules/mqtt/tests => tests/community/mqtt}/test_mosquitto.py (100%) rename {modules/mssql/tests => tests/community/mssql}/test_mssql.py (100%) rename {modules/mysql/tests => tests/community/mysql}/seeds/01-schema.sql (100%) rename {modules/mysql/tests => tests/community/mysql}/seeds/02-seeds.sql (100%) rename {modules/mysql/tests => tests/community/mysql}/test_mysql.py (100%) rename {modules/nats/tests => tests/community/nats}/test_nats.py (100%) rename {modules/nats/tests => tests/community/nats}/test_nats_jetstream.py (100%) rename {modules/neo4j/tests => tests/community/neo4j}/test_neo4j.py (100%) rename {modules/nginx/tests => tests/community/nginx}/test_nginx.py (100%) rename {modules/ollama/tests => tests/community/ollama}/test_ollama.py (100%) rename {modules/openfga/tests => tests/community/openfga}/test_openfga.py (100%) rename {modules/opensearch/tests => tests/community/opensearch}/test_opensearch.py (100%) rename {modules/oracle-free/tests => tests/community/oracle-free}/test_oracle.py (100%) rename {modules/postgres/tests => tests/community/postgres}/fixtures/postgres_create_example_table.sql (100%) rename {modules/postgres/tests => tests/community/postgres}/test_postgres.py (100%) rename {modules/qdrant/tests => tests/community/qdrant}/test_config.yaml (100%) rename {modules/qdrant/tests => tests/community/qdrant}/test_qdrant.py (100%) rename {modules/rabbitmq/tests => tests/community/rabbitmq}/test_rabbitmq.py (100%) rename {modules/redis/tests => tests/community/redis}/test_redis.py (100%) rename {modules/registry/tests => tests/community/registry}/test_registry.py (100%) rename {modules/scylla/tests => tests/community/scylla}/test_scylla.py (100%) rename {modules/selenium/tests => tests/community/selenium}/test_selenium.py (100%) rename {modules/sftp/tests => tests/community/sftp}/test_sftp.py (100%) rename {modules/trino/tests => tests/community/trino}/test_trino.py (100%) rename {modules/valkey/tests => tests/community/valkey}/test_valkey.py (100%) rename {modules/vault/tests => tests/community/vault}/test_vault.py (100%) rename {modules/weaviate/tests => tests/community/weaviate}/test_weaviate.py (100%) create mode 100644 tests/core/__init__.py rename {core/tests => tests/core}/_local_registry_container.py (100%) rename {core/tests => tests/core}/compose_fixtures/basic/docker-compose.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/basic/hello.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/basic_multiple/docker-compose.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/basic_volume/docker-compose.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/port_multiple/compose.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/port_single/compose.yaml (100%) rename {core/tests => tests/core}/compose_fixtures/profile_support/compose.yaml (100%) rename {core/tests => tests/core}/conftest.py (100%) rename {core/tests => tests/core}/image_fixtures/busybox/Dockerfile (100%) rename {core/tests => tests/core}/image_fixtures/sample/Dockerfile (100%) rename {core/tests => tests/core}/test_auth.py (100%) rename {core/tests => tests/core}/test_compose.py (100%) rename {core/tests => tests/core}/test_config.py (100%) rename {core/tests => tests/core}/test_container.py (100%) rename {core/tests => tests/core}/test_core.py (100%) rename {core/tests => tests/core}/test_core_ports.py (100%) rename {core/tests => tests/core}/test_core_registry.py (100%) rename {core/tests => tests/core}/test_docker_client.py (100%) rename {core/tests => tests/core}/test_docker_in_docker.py (100%) rename {core/tests => tests/core}/test_image.py (100%) rename {core/tests => tests/core}/test_inspect.py (100%) rename {core/tests => tests/core}/test_labels.py (100%) rename {core/tests => tests/core}/test_network.py (100%) rename {core/tests => tests/core}/test_new_docker_api.py (100%) rename {core/tests => tests/core}/test_protocol_compliance.py (100%) rename {core/tests => tests/core}/test_ryuk.py (100%) rename {core/tests => tests/core}/test_socat.py (100%) rename {core/tests => tests/core}/test_transferable.py (100%) rename {core/tests => tests/core}/test_utils.py (100%) rename {core/tests => tests/core}/test_version.py (100%) rename {core/tests => tests/core}/test_wait_strategies.py (100%) rename {core/tests => tests/core}/test_wait_strategies_integration.py (100%) rename {core/tests => tests/core}/test_waiting_utils.py (100%) diff --git a/.readthedocs.yml b/.readthedocs.yml index 7be4c7729..5747abd36 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,7 +4,7 @@ version: 2 sphinx: - configuration: conf.py + configuration: docs/conf.py build: os: ubuntu-24.04 diff --git a/Makefile b/Makefile index 9292a84df..46907cd25 100644 --- a/Makefile +++ b/Makefile @@ -44,11 +44,11 @@ mypy-core-report: ## Generate a report for mypy on the core package uv run mypy --config-file pyproject.toml core | uv run python scripts/mypy_report.py docs: ## Build the docs for the project - uv run --all-extras sphinx-build -nW . docs/_build + uv run --all-extras sphinx-build -nW docs docs/_build # Target to build docs watching for changes as per https://stackoverflow.com/a/21389615 docs-watch : - uv run sphinx-autobuild . docs/_build # requires 'pip install sphinx-autobuild' + uv run sphinx-autobuild docs docs/_build # requires 'pip install sphinx-autobuild' doctests: ${DOCTESTS} ## Run doctests found across the documentation. uv run --all-extras sphinx-build -b doctest . docs/_build diff --git a/core/tests/test_project_settings.py b/core/tests/test_project_settings.py deleted file mode 100644 index 553a9d4e3..000000000 --- a/core/tests/test_project_settings.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -This test contains sanity check for the project settings -""" - -import tomli # TODO: Migrate to tomllib once we drop 3.10 support -from pathlib import Path -from typing import Any, Mapping, Final - -import pytest - -PROJECT_DIR: Final = Path(__file__).resolve().parent.parent.parent - - -@pytest.fixture -def pyproject_toml() -> Mapping[str, Any]: - with open(PROJECT_DIR / "pyproject.toml", "rb") as f: - return tomli.load(f) - - -def test_wheel_dev_parity(pyproject_toml: Mapping[str, Any]) -> None: - """ - The build wheel should contain the same modules like the develop installation - - Because of our special layout we need two hatch settings for this. - - This tests ensures that both lead to the same results. - """ - - wheel_modules = set(pyproject_toml["tool"]["hatch"]["build"]["targets"]["wheel"]["packages"]) - - # src modules create .pth files in site-packages. This folders are added to - # sys.path, therefore they need no "testcontainers" part to work. - src_modules = { - f"{module}/testcontainers" - for module in pyproject_toml["tool"]["hatch"]["build"]["targets"]["wheel"]["dev-mode-dirs"] - } - assert wheel_modules == src_modules diff --git a/modules/arangodb/README.rst b/docs/community/arangodb.rst similarity index 100% rename from modules/arangodb/README.rst rename to docs/community/arangodb.rst diff --git a/modules/aws/README.rst b/docs/community/aws.rst similarity index 100% rename from modules/aws/README.rst rename to docs/community/aws.rst diff --git a/modules/azurite/README.rst b/docs/community/azurite.rst similarity index 100% rename from modules/azurite/README.rst rename to docs/community/azurite.rst diff --git a/modules/cassandra/README.rst b/docs/community/cassandra.rst similarity index 100% rename from modules/cassandra/README.rst rename to docs/community/cassandra.rst diff --git a/modules/chroma/README.rst b/docs/community/chroma.rst similarity index 100% rename from modules/chroma/README.rst rename to docs/community/chroma.rst diff --git a/modules/clickhouse/README.rst b/docs/community/clickhouse.rst similarity index 100% rename from modules/clickhouse/README.rst rename to docs/community/clickhouse.rst diff --git a/modules/cockroachdb/README.rst b/docs/community/cockroachdb.rst similarity index 100% rename from modules/cockroachdb/README.rst rename to docs/community/cockroachdb.rst diff --git a/modules/cosmosdb/README.rst b/docs/community/cosmosdb.rst similarity index 100% rename from modules/cosmosdb/README.rst rename to docs/community/cosmosdb.rst diff --git a/modules/db2/README.rst b/docs/community/db2.rst similarity index 100% rename from modules/db2/README.rst rename to docs/community/db2.rst diff --git a/modules/elasticsearch/README.rst b/docs/community/elasticsearch.rst similarity index 100% rename from modules/elasticsearch/README.rst rename to docs/community/elasticsearch.rst diff --git a/modules/generic/README.rst b/docs/community/generic.rst similarity index 100% rename from modules/generic/README.rst rename to docs/community/generic.rst diff --git a/modules/google/README.rst b/docs/community/google.rst similarity index 100% rename from modules/google/README.rst rename to docs/community/google.rst diff --git a/modules/influxdb/README.rst b/docs/community/influxdb.rst similarity index 100% rename from modules/influxdb/README.rst rename to docs/community/influxdb.rst diff --git a/modules/k3s/README.rst b/docs/community/k3s.rst similarity index 100% rename from modules/k3s/README.rst rename to docs/community/k3s.rst diff --git a/modules/kafka/README.rst b/docs/community/kafka.rst similarity index 100% rename from modules/kafka/README.rst rename to docs/community/kafka.rst diff --git a/modules/keycloak/README.rst b/docs/community/keycloak.rst similarity index 100% rename from modules/keycloak/README.rst rename to docs/community/keycloak.rst diff --git a/modules/localstack/README.rst b/docs/community/localstack.rst similarity index 100% rename from modules/localstack/README.rst rename to docs/community/localstack.rst diff --git a/modules/mailpit/README.rst b/docs/community/mailpit.rst similarity index 100% rename from modules/mailpit/README.rst rename to docs/community/mailpit.rst diff --git a/modules/memcached/README.rst b/docs/community/memcached.rst similarity index 100% rename from modules/memcached/README.rst rename to docs/community/memcached.rst diff --git a/modules/milvus/README.rst b/docs/community/milvus.rst similarity index 100% rename from modules/milvus/README.rst rename to docs/community/milvus.rst diff --git a/modules/minio/README.rst b/docs/community/minio.rst similarity index 100% rename from modules/minio/README.rst rename to docs/community/minio.rst diff --git a/modules/mongodb/README.rst b/docs/community/mongodb.rst similarity index 100% rename from modules/mongodb/README.rst rename to docs/community/mongodb.rst diff --git a/modules/mqtt/README.rst b/docs/community/mqtt.rst similarity index 100% rename from modules/mqtt/README.rst rename to docs/community/mqtt.rst diff --git a/modules/mssql/README.rst b/docs/community/mssql.rst similarity index 100% rename from modules/mssql/README.rst rename to docs/community/mssql.rst diff --git a/modules/mysql/README.rst b/docs/community/mysql.rst similarity index 100% rename from modules/mysql/README.rst rename to docs/community/mysql.rst diff --git a/modules/nats/README.rst b/docs/community/nats.rst similarity index 100% rename from modules/nats/README.rst rename to docs/community/nats.rst diff --git a/modules/neo4j/README.rst b/docs/community/neo4j.rst similarity index 100% rename from modules/neo4j/README.rst rename to docs/community/neo4j.rst diff --git a/modules/nginx/README.rst b/docs/community/nginx.rst similarity index 100% rename from modules/nginx/README.rst rename to docs/community/nginx.rst diff --git a/modules/ollama/README.rst b/docs/community/ollama.rst similarity index 100% rename from modules/ollama/README.rst rename to docs/community/ollama.rst diff --git a/modules/openfga/README.rst b/docs/community/openfga.rst similarity index 100% rename from modules/openfga/README.rst rename to docs/community/openfga.rst diff --git a/modules/opensearch/README.rst b/docs/community/opensearch.rst similarity index 100% rename from modules/opensearch/README.rst rename to docs/community/opensearch.rst diff --git a/modules/oracle-free/README.rst b/docs/community/oracle-free.rst similarity index 100% rename from modules/oracle-free/README.rst rename to docs/community/oracle-free.rst diff --git a/modules/postgres/README.rst b/docs/community/postgres.rst similarity index 100% rename from modules/postgres/README.rst rename to docs/community/postgres.rst diff --git a/modules/qdrant/README.rst b/docs/community/qdrant.rst similarity index 100% rename from modules/qdrant/README.rst rename to docs/community/qdrant.rst diff --git a/modules/rabbitmq/README.rst b/docs/community/rabbitmq.rst similarity index 100% rename from modules/rabbitmq/README.rst rename to docs/community/rabbitmq.rst diff --git a/modules/redis/README.rst b/docs/community/redis.rst similarity index 100% rename from modules/redis/README.rst rename to docs/community/redis.rst diff --git a/modules/registry/README.rst b/docs/community/registry.rst similarity index 100% rename from modules/registry/README.rst rename to docs/community/registry.rst diff --git a/modules/scylla/README.rst b/docs/community/scylla.rst similarity index 100% rename from modules/scylla/README.rst rename to docs/community/scylla.rst diff --git a/modules/selenium/README.rst b/docs/community/selenium.rst similarity index 100% rename from modules/selenium/README.rst rename to docs/community/selenium.rst diff --git a/modules/sftp/README.rst b/docs/community/sftp.rst similarity index 100% rename from modules/sftp/README.rst rename to docs/community/sftp.rst diff --git a/modules/trino/README.rst b/docs/community/trino.rst similarity index 100% rename from modules/trino/README.rst rename to docs/community/trino.rst diff --git a/modules/valkey/README.rst b/docs/community/valkey.rst similarity index 100% rename from modules/valkey/README.rst rename to docs/community/valkey.rst diff --git a/modules/vault/README.rst b/docs/community/vault.rst similarity index 100% rename from modules/vault/README.rst rename to docs/community/vault.rst diff --git a/modules/weaviate/README.rst b/docs/community/weaviate.rst similarity index 100% rename from modules/weaviate/README.rst rename to docs/community/weaviate.rst diff --git a/compose.rst b/docs/compose.rst similarity index 100% rename from compose.rst rename to docs/compose.rst diff --git a/conf.py b/docs/conf.py similarity index 100% rename from conf.py rename to docs/conf.py diff --git a/core/README.rst b/docs/core/README.rst similarity index 100% rename from core/README.rst rename to docs/core/README.rst diff --git a/index.rst b/docs/index.rst similarity index 99% rename from index.rst rename to docs/index.rst index 0facdd15b..eb7cf40f3 100644 --- a/index.rst +++ b/docs/index.rst @@ -17,7 +17,7 @@ testcontainers-python facilitates the use of Docker containers for functional an core/README compose - modules/index + community/* Getting Started --------------- diff --git a/docs/modules/arangodb.md b/docs/modules/arangodb.md index e342c9c9f..7cfe5d9ad 100644 --- a/docs/modules/arangodb.md +++ b/docs/modules/arangodb.md @@ -18,7 +18,7 @@ pip install testcontainers[arangodb] python-arango -[Creating an ArangoDB container](../../modules/arangodb/example_basic.py) +[Creating an ArangoDB container](arangodb_example.py) diff --git a/modules/arangodb/example_basic.py b/docs/modules/arangodb_example.py similarity index 100% rename from modules/arangodb/example_basic.py rename to docs/modules/arangodb_example.py diff --git a/docs/modules/aws.md b/docs/modules/aws.md index 8fb1ea412..6983c6975 100644 --- a/docs/modules/aws.md +++ b/docs/modules/aws.md @@ -18,6 +18,6 @@ pip install testcontainers[aws] httpx -[Creating an AWS container](../../modules/aws/example_basic.py) +[Creating an AWS container](aws_example.py) diff --git a/modules/aws/example_basic.py b/docs/modules/aws_example.py similarity index 100% rename from modules/aws/example_basic.py rename to docs/modules/aws_example.py diff --git a/docs/modules/azurite.md b/docs/modules/azurite.md index 9acc48730..d6b8fcec8 100644 --- a/docs/modules/azurite.md +++ b/docs/modules/azurite.md @@ -18,6 +18,6 @@ pip install testcontainers[azurite] azure-storage-blob -[Creating an Azurite container](../../modules/azurite/example_basic.py) +[Creating an Azurite container](azurite_example.py) diff --git a/modules/azurite/example_basic.py b/docs/modules/azurite_example.py similarity index 100% rename from modules/azurite/example_basic.py rename to docs/modules/azurite_example.py diff --git a/docs/modules/cassandra.md b/docs/modules/cassandra.md index 3250b737f..f903bcb4c 100644 --- a/docs/modules/cassandra.md +++ b/docs/modules/cassandra.md @@ -18,6 +18,6 @@ pip install testcontainers[cassandra] cassandra-driver -[Creating a Cassandra container](../../modules/cassandra/example_basic.py) +[Creating a Cassandra container](cassandra_example.py) diff --git a/modules/cassandra/example_basic.py b/docs/modules/cassandra_example.py similarity index 100% rename from modules/cassandra/example_basic.py rename to docs/modules/cassandra_example.py diff --git a/docs/modules/chroma.md b/docs/modules/chroma.md index ae2e45dcf..9df70af03 100644 --- a/docs/modules/chroma.md +++ b/docs/modules/chroma.md @@ -18,7 +18,7 @@ pip install testcontainers[chroma] chromadb requests -[Creating a Chroma container](../../modules/chroma/example_basic.py) +[Creating a Chroma container](chroma_example.py) diff --git a/modules/chroma/example_basic.py b/docs/modules/chroma_example.py similarity index 100% rename from modules/chroma/example_basic.py rename to docs/modules/chroma_example.py diff --git a/docs/modules/clickhouse.md b/docs/modules/clickhouse.md index ed86c3f32..f7eb7d31d 100644 --- a/docs/modules/clickhouse.md +++ b/docs/modules/clickhouse.md @@ -18,7 +18,7 @@ pip install testcontainers[clickhouse] clickhouse-driver -[Creating a ClickHouse container](../../modules/clickhouse/example_basic.py) +[Creating a ClickHouse container](clickhouse_example.py) diff --git a/modules/clickhouse/example_basic.py b/docs/modules/clickhouse_example.py similarity index 100% rename from modules/clickhouse/example_basic.py rename to docs/modules/clickhouse_example.py diff --git a/docs/modules/cockroachdb.md b/docs/modules/cockroachdb.md index 285ffc92d..2d353290c 100644 --- a/docs/modules/cockroachdb.md +++ b/docs/modules/cockroachdb.md @@ -18,7 +18,7 @@ pip install testcontainers[cockroachdb] sqlalchemy psycopg2 -[Creating a CockroachDB container](../../modules/cockroachdb/example_basic.py) +[Creating a CockroachDB container](cockroachdb_example.py) diff --git a/modules/cockroachdb/example_basic.py b/docs/modules/cockroachdb_example.py similarity index 100% rename from modules/cockroachdb/example_basic.py rename to docs/modules/cockroachdb_example.py diff --git a/docs/modules/cosmosdb.md b/docs/modules/cosmosdb.md index 3aadbe6b3..9c9aa494c 100644 --- a/docs/modules/cosmosdb.md +++ b/docs/modules/cosmosdb.md @@ -18,7 +18,7 @@ pip install testcontainers[cosmosdb] pymongo azure-cosmos -[Creating a CosmosDB container](../../modules/cosmosdb/example_basic.py) +[Creating a CosmosDB container](cosmosdb_example.py) diff --git a/modules/cosmosdb/example_basic.py b/docs/modules/cosmosdb_example.py similarity index 100% rename from modules/cosmosdb/example_basic.py rename to docs/modules/cosmosdb_example.py diff --git a/docs/modules/db2.md b/docs/modules/db2.md index 59b6a4493..c744fc9e4 100644 --- a/docs/modules/db2.md +++ b/docs/modules/db2.md @@ -18,7 +18,7 @@ pip install testcontainers[db2] sqlalchemy ibm-db -[Creating a DB2 container](../../modules/db2/example_basic.py) +[Creating a DB2 container](db2_example.py) diff --git a/modules/db2/example_basic.py b/docs/modules/db2_example.py similarity index 100% rename from modules/db2/example_basic.py rename to docs/modules/db2_example.py diff --git a/docs/modules/elasticsearch.md b/docs/modules/elasticsearch.md index e95e3beb5..1ba554520 100644 --- a/docs/modules/elasticsearch.md +++ b/docs/modules/elasticsearch.md @@ -18,6 +18,6 @@ pip install testcontainers[elasticsearch] -[Creating an Elasticsearch container](../../modules/elasticsearch/example_basic.py) +[Creating an Elasticsearch container](elasticsearch_example.py) diff --git a/modules/elasticsearch/example_basic.py b/docs/modules/elasticsearch_example.py similarity index 100% rename from modules/elasticsearch/example_basic.py rename to docs/modules/elasticsearch_example.py diff --git a/docs/modules/generic.md b/docs/modules/generic.md index 87d1209b7..ab2af61c0 100644 --- a/docs/modules/generic.md +++ b/docs/modules/generic.md @@ -18,6 +18,6 @@ pip install testcontainers[generic] -[Creating a Generic container](../../modules/generic/example_basic.py) +[Creating a Generic container](generic_example.py) diff --git a/modules/generic/example_basic.py b/docs/modules/generic_example.py similarity index 100% rename from modules/generic/example_basic.py rename to docs/modules/generic_example.py diff --git a/docs/modules/google.md b/docs/modules/google.md index f228e6c99..488034a2a 100644 --- a/docs/modules/google.md +++ b/docs/modules/google.md @@ -18,6 +18,6 @@ pip install testcontainers[google] google-cloud-datastore google-cloud-pubsub -[Creating a Google container](../../modules/google/example_basic.py) +[Creating a Google container](google_example.py) diff --git a/modules/google/example_basic.py b/docs/modules/google_example.py similarity index 100% rename from modules/google/example_basic.py rename to docs/modules/google_example.py diff --git a/docs/modules/influxdb.md b/docs/modules/influxdb.md index 9541db7a4..adba0da92 100644 --- a/docs/modules/influxdb.md +++ b/docs/modules/influxdb.md @@ -22,6 +22,6 @@ pip install testcontainers[influxdb] influxdb-client -[Creating an InfluxDB container](../../modules/influxdb/example_basic.py) +[Creating an InfluxDB container](influxdb_example.py) diff --git a/modules/influxdb/example_basic.py b/docs/modules/influxdb_example.py similarity index 100% rename from modules/influxdb/example_basic.py rename to docs/modules/influxdb_example.py diff --git a/docs/modules/k3s.md b/docs/modules/k3s.md index 66d26d0fc..638c7a5f7 100644 --- a/docs/modules/k3s.md +++ b/docs/modules/k3s.md @@ -18,6 +18,6 @@ pip install testcontainers[k3s] kubernetes pyyaml -[Creating a K3s container](../../modules/k3s/example_basic.py) +[Creating a K3s container](k3s_example.py) diff --git a/modules/k3s/example_basic.py b/docs/modules/k3s_example.py similarity index 100% rename from modules/k3s/example_basic.py rename to docs/modules/k3s_example.py diff --git a/docs/modules/kafka.md b/docs/modules/kafka.md index 3a206bb3d..202678447 100644 --- a/docs/modules/kafka.md +++ b/docs/modules/kafka.md @@ -18,6 +18,6 @@ pip install testcontainers[kafka] -[Creating a Kafka container](../../modules/kafka/example_basic.py) +[Creating a Kafka container](kafka_example.py) diff --git a/modules/kafka/example_basic.py b/docs/modules/kafka_example.py similarity index 100% rename from modules/kafka/example_basic.py rename to docs/modules/kafka_example.py diff --git a/docs/modules/keycloak.md b/docs/modules/keycloak.md index 98b638380..5be9ea0d7 100644 --- a/docs/modules/keycloak.md +++ b/docs/modules/keycloak.md @@ -18,6 +18,6 @@ pip install testcontainers[keycloak] python-keycloak requests -[Creating a Keycloak container](../../modules/keycloak/example_basic.py) +[Creating a Keycloak container](keycloak_example.py) diff --git a/modules/keycloak/example_basic.py b/docs/modules/keycloak_example.py similarity index 100% rename from modules/keycloak/example_basic.py rename to docs/modules/keycloak_example.py diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index 6c67d6696..72ded42fc 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -18,6 +18,6 @@ pip install testcontainers[localstack] boto3 -[Creating a LocalStack container](../../modules/localstack/example_basic.py) +[Creating a LocalStack container](localstack_example.py) diff --git a/modules/localstack/example_basic.py b/docs/modules/localstack_example.py similarity index 100% rename from modules/localstack/example_basic.py rename to docs/modules/localstack_example.py diff --git a/docs/modules/mailpit.md b/docs/modules/mailpit.md index ca7d49364..78ceb2991 100644 --- a/docs/modules/mailpit.md +++ b/docs/modules/mailpit.md @@ -18,6 +18,6 @@ pip install testcontainers[mailpit] cryptography -[Creating a Mailpit container](../../modules/mailpit/example_basic.py) +[Creating a Mailpit container](mailpit_example.py) diff --git a/modules/mailpit/example_basic.py b/docs/modules/mailpit_example.py similarity index 100% rename from modules/mailpit/example_basic.py rename to docs/modules/mailpit_example.py diff --git a/docs/modules/memcached.md b/docs/modules/memcached.md index 5d18fafc0..026ae9876 100644 --- a/docs/modules/memcached.md +++ b/docs/modules/memcached.md @@ -18,6 +18,6 @@ pip install testcontainers[memcached] pymemcache -[Creating a Memcached container](../../modules/memcached/example_basic.py) +[Creating a Memcached container](memcached_example.py) diff --git a/modules/memcached/example_basic.py b/docs/modules/memcached_example.py similarity index 100% rename from modules/memcached/example_basic.py rename to docs/modules/memcached_example.py diff --git a/docs/modules/milvus.md b/docs/modules/milvus.md index 9c7beda6d..9cfbddbc3 100644 --- a/docs/modules/milvus.md +++ b/docs/modules/milvus.md @@ -18,6 +18,6 @@ pip install testcontainers[milvus] requests -[Creating a Milvus container](../../modules/milvus/example_basic.py) +[Creating a Milvus container](milvus_example.py) diff --git a/modules/milvus/example_basic.py b/docs/modules/milvus_example.py similarity index 100% rename from modules/milvus/example_basic.py rename to docs/modules/milvus_example.py diff --git a/docs/modules/minio.md b/docs/modules/minio.md index 15ea1b6ef..414763a62 100644 --- a/docs/modules/minio.md +++ b/docs/modules/minio.md @@ -18,6 +18,6 @@ pip install testcontainers[minio] minio requests -[Creating a MinIO container](../../modules/minio/example_basic.py) +[Creating a MinIO container](minio_example.py) diff --git a/modules/minio/example_basic.py b/docs/modules/minio_example.py similarity index 100% rename from modules/minio/example_basic.py rename to docs/modules/minio_example.py diff --git a/docs/modules/mongodb.md b/docs/modules/mongodb.md index 0c2d2d75d..1b19ba2ec 100644 --- a/docs/modules/mongodb.md +++ b/docs/modules/mongodb.md @@ -18,6 +18,6 @@ pip install testcontainers[mongodb] pymongo -[Creating a MongoDB container](../../modules/mongodb/example_basic.py) +[Creating a MongoDB container](mongodb_example.py) diff --git a/modules/mongodb/example_basic.py b/docs/modules/mongodb_example.py similarity index 100% rename from modules/mongodb/example_basic.py rename to docs/modules/mongodb_example.py diff --git a/docs/modules/mqtt.md b/docs/modules/mqtt.md index c290532fd..2ed7c9d92 100644 --- a/docs/modules/mqtt.md +++ b/docs/modules/mqtt.md @@ -18,6 +18,6 @@ pip install testcontainers[mqtt] paho-mqtt -[Creating an MQTT container](../../modules/mqtt/example_basic.py) +[Creating an MQTT container](mqtt_example.py) diff --git a/modules/mqtt/example_basic.py b/docs/modules/mqtt_example.py similarity index 100% rename from modules/mqtt/example_basic.py rename to docs/modules/mqtt_example.py diff --git a/docs/modules/mssql.md b/docs/modules/mssql.md index effac8c75..7b806774c 100644 --- a/docs/modules/mssql.md +++ b/docs/modules/mssql.md @@ -18,6 +18,6 @@ pip install testcontainers[mssql] pymssql -[Creating an MSSQL container](../../modules/mssql/example_basic.py) +[Creating an MSSQL container](mssql_example.py) diff --git a/modules/mssql/example_basic.py b/docs/modules/mssql_example.py similarity index 100% rename from modules/mssql/example_basic.py rename to docs/modules/mssql_example.py diff --git a/docs/modules/mysql.md b/docs/modules/mysql.md index e3ca18ae7..993f8c850 100644 --- a/docs/modules/mysql.md +++ b/docs/modules/mysql.md @@ -18,6 +18,6 @@ pip install testcontainers[mysql] sqlalchemy pymysql -[Creating a MySQL container](../../modules/mysql/example_basic.py) +[Creating a MySQL container](mysql_example.py) diff --git a/modules/mysql/example_basic.py b/docs/modules/mysql_example.py similarity index 100% rename from modules/mysql/example_basic.py rename to docs/modules/mysql_example.py diff --git a/docs/modules/nats.md b/docs/modules/nats.md index e3616e490..927639213 100644 --- a/docs/modules/nats.md +++ b/docs/modules/nats.md @@ -18,6 +18,6 @@ pip install testcontainers[nats] nats-py -[Creating a NATS container](../../modules/nats/example_basic.py) +[Creating a NATS container](nats_example.py) diff --git a/modules/nats/example_basic.py b/docs/modules/nats_example.py similarity index 100% rename from modules/nats/example_basic.py rename to docs/modules/nats_example.py diff --git a/docs/modules/neo4j.md b/docs/modules/neo4j.md index 047dd1de3..971ade75f 100644 --- a/docs/modules/neo4j.md +++ b/docs/modules/neo4j.md @@ -18,6 +18,6 @@ pip install testcontainers[neo4j] neo4j -[Creating a Neo4j container](../../modules/neo4j/example_basic.py) +[Creating a Neo4j container](neo4j_example.py) diff --git a/modules/neo4j/example_basic.py b/docs/modules/neo4j_example.py similarity index 100% rename from modules/neo4j/example_basic.py rename to docs/modules/neo4j_example.py diff --git a/docs/modules/nginx.md b/docs/modules/nginx.md index 6781c1a88..2c2645c53 100644 --- a/docs/modules/nginx.md +++ b/docs/modules/nginx.md @@ -18,6 +18,6 @@ pip install testcontainers[nginx] -[Creating a Nginx container](../../modules/nginx/example_basic.py) +[Creating a Nginx container](nginx_example.py) diff --git a/modules/nginx/example_basic.py b/docs/modules/nginx_example.py similarity index 100% rename from modules/nginx/example_basic.py rename to docs/modules/nginx_example.py diff --git a/docs/modules/ollama.md b/docs/modules/ollama.md index c9db6e14f..0a92fa34d 100644 --- a/docs/modules/ollama.md +++ b/docs/modules/ollama.md @@ -18,6 +18,6 @@ pip install testcontainers[ollama] requests -[Creating an Ollama container](../../modules/ollama/example_basic.py) +[Creating an Ollama container](ollama_example.py) diff --git a/modules/ollama/example_basic.py b/docs/modules/ollama_example.py similarity index 100% rename from modules/ollama/example_basic.py rename to docs/modules/ollama_example.py diff --git a/docs/modules/opensearch.md b/docs/modules/opensearch.md index d57ee45a7..a9931b4aa 100644 --- a/docs/modules/opensearch.md +++ b/docs/modules/opensearch.md @@ -18,6 +18,6 @@ pip install testcontainers[opensearch] opensearch-py -[Creating an OpenSearch container](../../modules/opensearch/example_basic.py) +[Creating an OpenSearch container](opensearch_example.py) diff --git a/modules/azurite/testcontainers/azurite/py.typed b/docs/modules/opensearch_example.py similarity index 100% rename from modules/azurite/testcontainers/azurite/py.typed rename to docs/modules/opensearch_example.py diff --git a/docs/modules/oracle-free.md b/docs/modules/oracle-free.md index a0b68d18d..87e5e9b84 100644 --- a/docs/modules/oracle-free.md +++ b/docs/modules/oracle-free.md @@ -18,6 +18,6 @@ pip install testcontainers[oracle-free] -[Creating an Oracle Free container](../../modules/oracle-free/example_basic.py) +[Creating an Oracle Free container](oracle-free_example.py) diff --git a/modules/oracle-free/example_basic.py b/docs/modules/oracle-free_example.py similarity index 100% rename from modules/oracle-free/example_basic.py rename to docs/modules/oracle-free_example.py diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 4b381753f..9d6769af8 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -18,6 +18,6 @@ pip install testcontainers[postgres] sqlalchemy psycopg2 -[Creating a PostgreSQL container](../../modules/postgres/example_basic.py) +[Creating a PostgreSQL container](postgres_example.py) diff --git a/modules/postgres/example_basic.py b/docs/modules/postgres_example.py similarity index 100% rename from modules/postgres/example_basic.py rename to docs/modules/postgres_example.py diff --git a/docs/modules/qdrant.md b/docs/modules/qdrant.md index c4eb7310f..2d53146ba 100644 --- a/docs/modules/qdrant.md +++ b/docs/modules/qdrant.md @@ -18,6 +18,6 @@ pip install testcontainers[qdrant] -[Creating a Qdrant container](../../modules/qdrant/example_basic.py) +[Creating a Qdrant container](qdrant_example.py) diff --git a/modules/qdrant/example_basic.py b/docs/modules/qdrant_example.py similarity index 100% rename from modules/qdrant/example_basic.py rename to docs/modules/qdrant_example.py diff --git a/docs/modules/rabbitmq.md b/docs/modules/rabbitmq.md index 850b2739f..ec754d984 100644 --- a/docs/modules/rabbitmq.md +++ b/docs/modules/rabbitmq.md @@ -18,6 +18,6 @@ pip install testcontainers[rabbitmq] pika -[Creating a RabbitMQ container](../../modules/rabbitmq/example_basic.py) +[Creating a RabbitMQ container](rabbitmq_example.py) diff --git a/modules/rabbitmq/example_basic.py b/docs/modules/rabbitmq_example.py similarity index 100% rename from modules/rabbitmq/example_basic.py rename to docs/modules/rabbitmq_example.py diff --git a/docs/modules/redis.md b/docs/modules/redis.md index 16f8566e2..aced9cbdf 100644 --- a/docs/modules/redis.md +++ b/docs/modules/redis.md @@ -18,6 +18,6 @@ pip install testcontainers[redis] redis -[Creating a Redis container](../../modules/redis/example_basic.py) +[Creating a Redis container](redis_example.py) diff --git a/modules/redis/example_basic.py b/docs/modules/redis_example.py similarity index 100% rename from modules/redis/example_basic.py rename to docs/modules/redis_example.py diff --git a/docs/modules/registry.md b/docs/modules/registry.md index b00380d8c..cc160b91c 100644 --- a/docs/modules/registry.md +++ b/docs/modules/registry.md @@ -18,6 +18,6 @@ pip install testcontainers[registry] -[Creating a Registry container](../../modules/registry/example_basic.py) +[Creating a Registry container](registry_example.py) diff --git a/modules/registry/example_basic.py b/docs/modules/registry_example.py similarity index 100% rename from modules/registry/example_basic.py rename to docs/modules/registry_example.py diff --git a/docs/modules/scylla.md b/docs/modules/scylla.md index c1001a425..48fbfff6c 100644 --- a/docs/modules/scylla.md +++ b/docs/modules/scylla.md @@ -18,6 +18,6 @@ pip install testcontainers[scylla] -[Creating a Scylla container](../../modules/scylla/example_basic.py) +[Creating a Scylla container](scylla_example.py) diff --git a/modules/scylla/example_basic.py b/docs/modules/scylla_example.py similarity index 100% rename from modules/scylla/example_basic.py rename to docs/modules/scylla_example.py diff --git a/docs/modules/selenium.md b/docs/modules/selenium.md index 68b6174a7..9d07effb4 100644 --- a/docs/modules/selenium.md +++ b/docs/modules/selenium.md @@ -18,6 +18,6 @@ pip install testcontainers[selenium] selenium urllib3 -[Creating a Selenium container](../../modules/selenium/example_basic.py) +[Creating a Selenium container](selenium_example.py) diff --git a/modules/selenium/example_basic.py b/docs/modules/selenium_example.py similarity index 100% rename from modules/selenium/example_basic.py rename to docs/modules/selenium_example.py diff --git a/docs/modules/sftp.md b/docs/modules/sftp.md index 8fe7ecc5c..cbe1b3f99 100644 --- a/docs/modules/sftp.md +++ b/docs/modules/sftp.md @@ -18,6 +18,6 @@ pip install testcontainers[sftp] paramiko cryptography -[Creating an SFTP container](../../modules/sftp/example_basic.py) +[Creating an SFTP container](sftp_example.py) diff --git a/modules/sftp/example_basic.py b/docs/modules/sftp_example.py similarity index 100% rename from modules/sftp/example_basic.py rename to docs/modules/sftp_example.py diff --git a/docs/modules/test_module_import.md b/docs/modules/test_module_import.md deleted file mode 100644 index ed5472756..000000000 --- a/docs/modules/test_module_import.md +++ /dev/null @@ -1,100 +0,0 @@ -# Test Module Import - -Since testcontainers-python :material-tag: v4.7.0 - -## Introduction - -The Testcontainers module for testing Python module imports and package management. This module provides a containerized environment for testing various aspects of Python module imports, including: - -- Basic module and package imports -- Module reloading -- Version-specific imports -- Dependencies and environment variables -- Advanced features like custom loaders and namespace packages - -## Adding this module to your project dependencies - -Please run the following command to add the Test Module Import module to your python dependencies: - -``` -pip install testcontainers[test_module_import] -``` - -## Usage examples - -The module provides several examples demonstrating different use cases: - -### Basic Module Imports - -This example demonstrates the fundamental capabilities of the TestModuleImportContainer: - -- Importing a basic Python module and accessing its attributes -- Importing and using submodules -- Importing and working with packages -- Proper cleanup of imported modules - - - -[Basic module imports](../../modules/test_module_import/examples/01_basic_import.py) - - - -### Module Reloading - -This example shows how to work with module reloading functionality: - -- Importing a module and accessing its initial state -- Reloading the module to pick up changes -- Handling reloading errors gracefully -- Managing module state during reloads - - - -[Module reloading](../../modules/test_module_import/examples/02_module_reloading.py) - - - -### Version-Specific Imports - -This example demonstrates handling version-specific module imports: - -- Importing specific versions of modules -- Managing version compatibility -- Accessing and verifying version information -- Working with version-specific features - - - -[Version-specific imports](../../modules/test_module_import/examples/03_version_specific.py) - - - -### Dependencies and Environment Variables - -This example shows how to handle module dependencies and environment requirements: - -- Importing modules with external dependencies -- Managing required dependency versions -- Setting up and accessing environment variables -- Handling environment-specific configurations - - - -[Dependencies and environment variables](../../modules/test_module_import/examples/04_dependencies_and_env.py) - - - -### Advanced Features - -This example demonstrates advanced module import capabilities: - -- Using custom module loaders for specialized import scenarios -- Working with namespace packages -- Managing entry points -- Handling complex module configurations - - - -[Advanced features](../../modules/test_module_import/examples/05_advanced_features.py) - - diff --git a/docs/modules/trino.md b/docs/modules/trino.md index 3ceda1445..89d724809 100644 --- a/docs/modules/trino.md +++ b/docs/modules/trino.md @@ -18,6 +18,6 @@ pip install testcontainers[trino] trino -[Creating a Trino container](../../modules/trino/example_basic.py) +[Creating a Trino container](trino_example.py) diff --git a/modules/trino/example_basic.py b/docs/modules/trino_example.py similarity index 100% rename from modules/trino/example_basic.py rename to docs/modules/trino_example.py diff --git a/docs/modules/valkey.md b/docs/modules/valkey.md index d71182fac..db45a7fcb 100644 --- a/docs/modules/valkey.md +++ b/docs/modules/valkey.md @@ -18,6 +18,6 @@ pip install testcontainers[valkey] -[Creating a Valkey container](../../modules/valkey/example_basic.py) +[Creating a Valkey container](valkey_example.py) diff --git a/modules/valkey/example_basic.py b/docs/modules/valkey_example.py similarity index 100% rename from modules/valkey/example_basic.py rename to docs/modules/valkey_example.py diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 7dc4d1260..139c88af4 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -18,6 +18,6 @@ pip install testcontainers[vault] hvac -[Creating a Vault container](../../modules/vault/example_basic.py) +[Creating a Vault container](vault_example.py) diff --git a/modules/vault/example_basic.py b/docs/modules/vault_example.py similarity index 100% rename from modules/vault/example_basic.py rename to docs/modules/vault_example.py diff --git a/docs/modules/weaviate.md b/docs/modules/weaviate.md index 90fec975a..83ae7e22f 100644 --- a/docs/modules/weaviate.md +++ b/docs/modules/weaviate.md @@ -18,6 +18,6 @@ pip install testcontainers[weaviate] weaviate-client -[Creating a Weaviate container](../../modules/weaviate/example_basic.py) +[Creating a Weaviate container](weaviate_example.py) diff --git a/modules/weaviate/example_basic.py b/docs/modules/weaviate_example.py similarity index 100% rename from modules/weaviate/example_basic.py rename to docs/modules/weaviate_example.py diff --git a/modules/test_module_import/README.rst b/modules/test_module_import/README.rst deleted file mode 100644 index 3d2e7543a..000000000 --- a/modules/test_module_import/README.rst +++ /dev/null @@ -1,2 +0,0 @@ -.. autoclass:: testcontainers.test_module_import.NewSubModuleContainer -.. title:: testcontainers.test_module_import.NewSubModuleContainer diff --git a/modules/test_module_import/examples/01_basic_import.py b/modules/test_module_import/examples/01_basic_import.py deleted file mode 100644 index 9068c9944..000000000 --- a/modules/test_module_import/examples/01_basic_import.py +++ /dev/null @@ -1,58 +0,0 @@ -import sys -from pathlib import Path - -from testcontainers.test_module_import import TestModuleImportContainer - - -def test_module_import(): - try: - import test_module - - print("\nSuccessfully imported test_module") - print(f"Module version: {test_module.__version__}") - print(f"Module description: {test_module.__description__}") - except ImportError as e: - print(f"\nFailed to import test_module: {e}") - - -def test_submodule_import(): - try: - from test_module import submodule - - print("\nSuccessfully imported test_module.submodule") - print(f"Submodule function result: {submodule.test_function()}") - except ImportError as e: - print(f"\nFailed to import test_module.submodule: {e}") - - -def test_package_import(): - try: - import test_package - - print("\nSuccessfully imported test_package") - print(f"Package version: {test_package.__version__}") - except ImportError as e: - print(f"\nFailed to import test_package: {e}") - - -def basic_example(): - with TestModuleImportContainer(): - # Add test module to Python path - sys.path.append(str(Path(__file__).parent)) - print("Added test module to Python path") - - # Test various imports - test_module_import() - test_submodule_import() - test_package_import() - - # Clean up - if "test_module" in sys.modules: - del sys.modules["test_module"] - if "test_package" in sys.modules: - del sys.modules["test_package"] - print("\nCleaned up imported modules") - - -if __name__ == "__main__": - basic_example() diff --git a/modules/test_module_import/examples/02_module_reloading.py b/modules/test_module_import/examples/02_module_reloading.py deleted file mode 100644 index 4e05ff8bd..000000000 --- a/modules/test_module_import/examples/02_module_reloading.py +++ /dev/null @@ -1,41 +0,0 @@ -import importlib -import sys -from pathlib import Path - -from testcontainers.test_module_import import TestModuleImportContainer - - -def test_module_reloading(): - try: - import test_module - - print("\nSuccessfully imported test_module") - print(f"Initial version: {test_module.__version__}") - - # Simulate module changes by reloading - importlib.reload(test_module) - print("\nSuccessfully reloaded test_module") - print(f"Updated version: {test_module.__version__}") - except ImportError as e: - print(f"\nFailed to import test_module: {e}") - except NameError: - print("\nCould not reload test_module (not imported)") - - -def reloading_example(): - with TestModuleImportContainer(): - # Add test module to Python path - sys.path.append(str(Path(__file__).parent)) - print("Added test module to Python path") - - # Test module reloading - test_module_reloading() - - # Clean up - if "test_module" in sys.modules: - del sys.modules["test_module"] - print("\nCleaned up imported modules") - - -if __name__ == "__main__": - reloading_example() diff --git a/modules/test_module_import/examples/03_version_specific.py b/modules/test_module_import/examples/03_version_specific.py deleted file mode 100644 index b24a6b47e..000000000 --- a/modules/test_module_import/examples/03_version_specific.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys -from pathlib import Path - -from testcontainers.test_module_import import TestModuleImportContainer - - -def test_version_import(): - try: - import test_module_v2 - - print("\nSuccessfully imported test_module_v2") - print(f"Module version: {test_module_v2.__version__}") - print(f"Module features: {test_module_v2.FEATURES}") - except ImportError as e: - print(f"\nFailed to import test_module_v2: {e}") - - -def version_example(): - with TestModuleImportContainer(): - # Add test module to Python path - sys.path.append(str(Path(__file__).parent)) - print("Added test module to Python path") - - # Test version-specific imports - test_version_import() - - # Clean up - if "test_module_v2" in sys.modules: - del sys.modules["test_module_v2"] - print("\nCleaned up imported modules") - - -if __name__ == "__main__": - version_example() diff --git a/modules/test_module_import/examples/04_dependencies_and_env.py b/modules/test_module_import/examples/04_dependencies_and_env.py deleted file mode 100644 index de49fc55b..000000000 --- a/modules/test_module_import/examples/04_dependencies_and_env.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys -from pathlib import Path - -from testcontainers.test_module_import import TestModuleImportContainer - - -def test_deps_import(): - try: - import test_module_with_deps - - print("\nSuccessfully imported test_module_with_deps") - print(f"Dependencies: {test_module_with_deps.DEPENDENCIES}") - print(f"Required versions: {test_module_with_deps.REQUIRED_VERSIONS}") - except ImportError as e: - print(f"\nFailed to import test_module_with_deps: {e}") - - -def test_env_import(): - try: - import test_module_with_env - - print("\nSuccessfully imported test_module_with_env") - print(f"Environment variables: {test_module_with_env.ENV_VARS}") - print(f"Environment values: {test_module_with_env.ENV_VALUES}") - except ImportError as e: - print(f"\nFailed to import test_module_with_env: {e}") - - -def deps_and_env_example(): - with TestModuleImportContainer(): - # Add test module to Python path - sys.path.append(str(Path(__file__).parent)) - print("Added test module to Python path") - - # Test dependencies and environment imports - test_deps_import() - test_env_import() - - # Clean up - if "test_module_with_deps" in sys.modules: - del sys.modules["test_module_with_deps"] - if "test_module_with_env" in sys.modules: - del sys.modules["test_module_with_env"] - print("\nCleaned up imported modules") - - -if __name__ == "__main__": - deps_and_env_example() diff --git a/modules/test_module_import/examples/05_advanced_features.py b/modules/test_module_import/examples/05_advanced_features.py deleted file mode 100644 index 45c24faa8..000000000 --- a/modules/test_module_import/examples/05_advanced_features.py +++ /dev/null @@ -1,59 +0,0 @@ -import sys -from pathlib import Path - -from testcontainers.test_module_import import TestModuleImportContainer - - -def test_custom_loader_import(): - try: - import test_module_custom_loader - - print("\nSuccessfully imported test_module_custom_loader") - print(f"Loader type: {test_module_custom_loader.LOADER_TYPE}") - print(f"Loader configuration: {test_module_custom_loader.LOADER_CONFIG}") - except ImportError as e: - print(f"\nFailed to import test_module_custom_loader: {e}") - - -def test_namespace_import(): - try: - import test_namespace_package - - print("\nSuccessfully imported test_namespace_package") - print(f"Namespace: {test_namespace_package.__namespace__}") - print(f"Available subpackages: {test_namespace_package.SUBPACKAGES}") - except ImportError as e: - print(f"\nFailed to import test_namespace_package: {e}") - - -def test_entry_points_import(): - try: - import test_module_with_entry_points - - print("\nSuccessfully imported test_module_with_entry_points") - print(f"Entry points: {test_module_with_entry_points.ENTRY_POINTS}") - print(f"Entry point groups: {test_module_with_entry_points.ENTRY_POINT_GROUPS}") - except ImportError as e: - print(f"\nFailed to import test_module_with_entry_points: {e}") - - -def advanced_features_example(): - with TestModuleImportContainer(): - # Add test module to Python path - sys.path.append(str(Path(__file__).parent)) - print("Added test module to Python path") - - # Test advanced features - test_custom_loader_import() - test_namespace_import() - test_entry_points_import() - - # Clean up - for module in ["test_module_custom_loader", "test_namespace_package", "test_module_with_entry_points"]: - if module in sys.modules: - del sys.modules[module] - print("\nCleaned up imported modules") - - -if __name__ == "__main__": - advanced_features_example() diff --git a/modules/test_module_import/testcontainers/test_module_import/__init__.py b/modules/test_module_import/testcontainers/test_module_import/__init__.py deleted file mode 100644 index 74074699e..000000000 --- a/modules/test_module_import/testcontainers/test_module_import/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .new_sub_module import NewSubModuleContainer # noqa: F401 diff --git a/modules/test_module_import/testcontainers/test_module_import/new_sub_module.py b/modules/test_module_import/testcontainers/test_module_import/new_sub_module.py deleted file mode 100644 index 6f25d2777..000000000 --- a/modules/test_module_import/testcontainers/test_module_import/new_sub_module.py +++ /dev/null @@ -1,27 +0,0 @@ -from testcontainers.generic.server import ServerContainer - - -class NewSubModuleContainer(ServerContainer): - """ - This class is a mock container for testing purposes. It is used to test importing from other modules. - - .. doctest:: - - >>> import httpx - >>> from testcontainers.core.image import DockerImage - >>> from testcontainers.test_module_import import NewSubModuleContainer - - >>> with DockerImage(path="./modules/generic/tests/samples/python_server", tag="test-new-mod:latest") as image: - ... with NewSubModuleContainer(port=9000, image=image) as new_mod: - ... url = new_mod._create_connection_url() - ... response = httpx.get(f"{url}", timeout=5) - ... assert response.status_code == 200, "Response status code is not 200" - ... assert new_mod.additional_capability() == "NewSubModuleContainer" - - """ - - def __init__(self, port: int, image: str) -> None: - super().__init__(port, image) - - def additional_capability(self) -> str: - return "NewSubModuleContainer" diff --git a/modules/test_module_import/tests/test_mock_one.py b/modules/test_module_import/tests/test_mock_one.py deleted file mode 100644 index 915c95b11..000000000 --- a/modules/test_module_import/tests/test_mock_one.py +++ /dev/null @@ -1,14 +0,0 @@ -import httpx - -from testcontainers.core.waiting_utils import wait_for_logs -from testcontainers.core.image import DockerImage -from testcontainers.test_module_import import NewSubModuleContainer - - -def test_like_doctest(): - with DockerImage(path="./modules/generic/tests/samples/python_server", tag="test-new-mod:latest") as image: - with NewSubModuleContainer(port=9000, image=image) as new_mod: - url = new_mod._create_connection_url() - response = httpx.get(f"{url}", timeout=5) - assert response.status_code == 200, "Response status code is not 200" - assert new_mod.additional_capability() == "NewSubModuleContainer" diff --git a/pyproject.toml b/pyproject.toml index c2065b264..ab53ea7a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -172,111 +172,19 @@ include = [ "pyproject.toml", ] -# TODO: Remove all this below once the project moved to `src` layout + [tool.hatch.build.targets.wheel] -# Mirror Poetry's multi-source package includes. Each path contains a "testcontainers" package. packages = [ - "core/testcontainers", - "modules/arangodb/testcontainers", - "modules/aws/testcontainers", - "modules/azurite/testcontainers", - "modules/cassandra/testcontainers", - "modules/chroma/testcontainers", - "modules/clickhouse/testcontainers", - "modules/cockroachdb/testcontainers", - "modules/cosmosdb/testcontainers", - "modules/db2/testcontainers", - "modules/elasticsearch/testcontainers", - "modules/generic/testcontainers", - "modules/test_module_import/testcontainers", - "modules/google/testcontainers", - "modules/influxdb/testcontainers", - "modules/k3s/testcontainers", - "modules/kafka/testcontainers", - "modules/keycloak/testcontainers", - "modules/localstack/testcontainers", - "modules/mailpit/testcontainers", - "modules/memcached/testcontainers", - "modules/minio/testcontainers", - "modules/milvus/testcontainers", - "modules/mongodb/testcontainers", - "modules/mqtt/testcontainers", - "modules/mssql/testcontainers", - "modules/mysql/testcontainers", - "modules/nats/testcontainers", - "modules/neo4j/testcontainers", - "modules/nginx/testcontainers", - "modules/ollama/testcontainers", - "modules/openfga/testcontainers", - "modules/opensearch/testcontainers", - "modules/oracle-free/testcontainers", - "modules/postgres/testcontainers", - "modules/qdrant/testcontainers", - "modules/rabbitmq/testcontainers", - "modules/redis/testcontainers", - "modules/registry/testcontainers", - "modules/sftp/testcontainers", - "modules/selenium/testcontainers", - "modules/scylla/testcontainers", - "modules/trino/testcontainers", - "modules/valkey/testcontainers", - "modules/vault/testcontainers", - "modules/weaviate/testcontainers", -] -# Yeah we need to duplicate this folders here for hatchling to work, really waiting for src layout -# There is a test that ensures both list are identical -dev-mode-dirs = [ - "core", - "modules/arangodb", - "modules/aws", - "modules/azurite", - "modules/cassandra", - "modules/chroma", - "modules/clickhouse", - "modules/cockroachdb", - "modules/cosmosdb", - "modules/db2", - "modules/elasticsearch", - "modules/generic", - "modules/test_module_import", - "modules/google", - "modules/influxdb", - "modules/k3s", - "modules/kafka", - "modules/keycloak", - "modules/localstack", - "modules/mailpit", - "modules/memcached", - "modules/minio", - "modules/milvus", - "modules/mongodb", - "modules/mqtt", - "modules/mssql", - "modules/mysql", - "modules/nats", - "modules/neo4j", - "modules/nginx", - "modules/ollama", - "modules/openfga", - "modules/opensearch", - "modules/oracle-free", - "modules/postgres", - "modules/qdrant", - "modules/rabbitmq", - "modules/redis", - "modules/registry", - "modules/sftp", - "modules/selenium", - "modules/scylla", - "modules/trino", - "modules/valkey", - "modules/vault", - "modules/weaviate", + "src/testcontainers", ] # ---------- Tool configs---------- [tool.pytest.ini_options] -addopts = "--tb=short --strict-markers" +addopts = [ + "--tb=short", + "--strict-markers", + "--import-mode=importlib", # https://docs.pytest.org/en/stable/explanation/goodpractices.html#choosing-a-test-layout +] log_cli = true log_cli_level = "INFO" markers = [ @@ -303,7 +211,9 @@ exclude_lines = [ [tool.coverage.paths] source = [ - "core/testcontainers", + "src/testcontainers/core", + "src/testcontainers/compose", + "src/testcontainers/cocat", "*/site-packages/testcontainers", "*/dist-packages/testcontainers", ] @@ -312,7 +222,6 @@ source = [ target-version = "py39" line-length = 120 fix = true -src = ["core", "modules/*"] [tool.ruff.lint] fixable = ["I"] diff --git a/modules/generic/tests/samples/advance_1/app/__init__.py b/src/testcontainers/__init__.py similarity index 100% rename from modules/generic/tests/samples/advance_1/app/__init__.py rename to src/testcontainers/__init__.py diff --git a/modules/arangodb/testcontainers/arangodb/__init__.py b/src/testcontainers/community/arangodb/__init__.py similarity index 100% rename from modules/arangodb/testcontainers/arangodb/__init__.py rename to src/testcontainers/community/arangodb/__init__.py diff --git a/modules/aws/testcontainers/aws/__init__.py b/src/testcontainers/community/aws/__init__.py similarity index 100% rename from modules/aws/testcontainers/aws/__init__.py rename to src/testcontainers/community/aws/__init__.py diff --git a/modules/aws/testcontainers/aws/aws_lambda.py b/src/testcontainers/community/aws/aws_lambda.py similarity index 100% rename from modules/aws/testcontainers/aws/aws_lambda.py rename to src/testcontainers/community/aws/aws_lambda.py diff --git a/modules/azurite/testcontainers/azurite/__init__.py b/src/testcontainers/community/azurite/__init__.py similarity index 100% rename from modules/azurite/testcontainers/azurite/__init__.py rename to src/testcontainers/community/azurite/__init__.py diff --git a/modules/mailpit/testcontainers/mailpit/py.typed b/src/testcontainers/community/azurite/py.typed similarity index 100% rename from modules/mailpit/testcontainers/mailpit/py.typed rename to src/testcontainers/community/azurite/py.typed diff --git a/modules/cassandra/testcontainers/cassandra/__init__.py b/src/testcontainers/community/cassandra/__init__.py similarity index 100% rename from modules/cassandra/testcontainers/cassandra/__init__.py rename to src/testcontainers/community/cassandra/__init__.py diff --git a/modules/chroma/testcontainers/chroma/__init__.py b/src/testcontainers/community/chroma/__init__.py similarity index 100% rename from modules/chroma/testcontainers/chroma/__init__.py rename to src/testcontainers/community/chroma/__init__.py diff --git a/modules/clickhouse/testcontainers/clickhouse/__init__.py b/src/testcontainers/community/clickhouse/__init__.py similarity index 100% rename from modules/clickhouse/testcontainers/clickhouse/__init__.py rename to src/testcontainers/community/clickhouse/__init__.py diff --git a/modules/cockroachdb/testcontainers/cockroachdb/__init__.py b/src/testcontainers/community/cockroachdb/__init__.py similarity index 100% rename from modules/cockroachdb/testcontainers/cockroachdb/__init__.py rename to src/testcontainers/community/cockroachdb/__init__.py diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/src/testcontainers/community/cosmosdb/__init__.py similarity index 100% rename from modules/cosmosdb/testcontainers/cosmosdb/__init__.py rename to src/testcontainers/community/cosmosdb/__init__.py diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py b/src/testcontainers/community/cosmosdb/_emulator.py similarity index 100% rename from modules/cosmosdb/testcontainers/cosmosdb/_emulator.py rename to src/testcontainers/community/cosmosdb/_emulator.py diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py b/src/testcontainers/community/cosmosdb/_grab.py similarity index 100% rename from modules/cosmosdb/testcontainers/cosmosdb/_grab.py rename to src/testcontainers/community/cosmosdb/_grab.py diff --git a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py b/src/testcontainers/community/cosmosdb/mongodb.py similarity index 100% rename from modules/cosmosdb/testcontainers/cosmosdb/mongodb.py rename to src/testcontainers/community/cosmosdb/mongodb.py diff --git a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py b/src/testcontainers/community/cosmosdb/nosql.py similarity index 100% rename from modules/cosmosdb/testcontainers/cosmosdb/nosql.py rename to src/testcontainers/community/cosmosdb/nosql.py diff --git a/modules/db2/testcontainers/db2/__init__.py b/src/testcontainers/community/db2/__init__.py similarity index 100% rename from modules/db2/testcontainers/db2/__init__.py rename to src/testcontainers/community/db2/__init__.py diff --git a/modules/elasticsearch/testcontainers/elasticsearch/__init__.py b/src/testcontainers/community/elasticsearch/__init__.py similarity index 100% rename from modules/elasticsearch/testcontainers/elasticsearch/__init__.py rename to src/testcontainers/community/elasticsearch/__init__.py diff --git a/modules/generic/testcontainers/generic/__init__.py b/src/testcontainers/community/generic/__init__.py similarity index 100% rename from modules/generic/testcontainers/generic/__init__.py rename to src/testcontainers/community/generic/__init__.py diff --git a/modules/generic/testcontainers/generic/providers/__init__.py b/src/testcontainers/community/generic/providers/__init__.py similarity index 100% rename from modules/generic/testcontainers/generic/providers/__init__.py rename to src/testcontainers/community/generic/providers/__init__.py diff --git a/modules/generic/testcontainers/generic/providers/sql_connection_wait_strategy.py b/src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py similarity index 100% rename from modules/generic/testcontainers/generic/providers/sql_connection_wait_strategy.py rename to src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py diff --git a/modules/generic/testcontainers/generic/server.py b/src/testcontainers/community/generic/server.py similarity index 99% rename from modules/generic/testcontainers/generic/server.py rename to src/testcontainers/community/generic/server.py index efbd343a9..19a7c896c 100644 --- a/modules/generic/testcontainers/generic/server.py +++ b/src/testcontainers/community/generic/server.py @@ -1,7 +1,6 @@ from typing import Union import httpx - from testcontainers.core.container import DockerContainer from testcontainers.core.exceptions import ContainerStartException from testcontainers.core.image import DockerImage diff --git a/modules/generic/testcontainers/generic/sql.py b/src/testcontainers/community/generic/sql.py similarity index 100% rename from modules/generic/testcontainers/generic/sql.py rename to src/testcontainers/community/generic/sql.py diff --git a/modules/google/testcontainers/google/__init__.py b/src/testcontainers/community/google/__init__.py similarity index 100% rename from modules/google/testcontainers/google/__init__.py rename to src/testcontainers/community/google/__init__.py diff --git a/modules/google/testcontainers/google/datastore.py b/src/testcontainers/community/google/datastore.py similarity index 100% rename from modules/google/testcontainers/google/datastore.py rename to src/testcontainers/community/google/datastore.py diff --git a/modules/google/testcontainers/google/pubsub.py b/src/testcontainers/community/google/pubsub.py similarity index 100% rename from modules/google/testcontainers/google/pubsub.py rename to src/testcontainers/community/google/pubsub.py diff --git a/modules/influxdb/testcontainers/influxdb.py b/src/testcontainers/community/influxdb/__init__.py similarity index 100% rename from modules/influxdb/testcontainers/influxdb.py rename to src/testcontainers/community/influxdb/__init__.py diff --git a/modules/influxdb/testcontainers/influxdb1/__init__.py b/src/testcontainers/community/influxdb1/__init__.py similarity index 100% rename from modules/influxdb/testcontainers/influxdb1/__init__.py rename to src/testcontainers/community/influxdb1/__init__.py diff --git a/modules/influxdb/testcontainers/influxdb2/__init__.py b/src/testcontainers/community/influxdb2/__init__.py similarity index 100% rename from modules/influxdb/testcontainers/influxdb2/__init__.py rename to src/testcontainers/community/influxdb2/__init__.py diff --git a/modules/k3s/testcontainers/k3s/__init__.py b/src/testcontainers/community/k3s/__init__.py similarity index 100% rename from modules/k3s/testcontainers/k3s/__init__.py rename to src/testcontainers/community/k3s/__init__.py diff --git a/modules/kafka/testcontainers/kafka/__init__.py b/src/testcontainers/community/kafka/__init__.py similarity index 100% rename from modules/kafka/testcontainers/kafka/__init__.py rename to src/testcontainers/community/kafka/__init__.py diff --git a/modules/kafka/testcontainers/kafka/_redpanda.py b/src/testcontainers/community/kafka/_redpanda.py similarity index 100% rename from modules/kafka/testcontainers/kafka/_redpanda.py rename to src/testcontainers/community/kafka/_redpanda.py diff --git a/modules/keycloak/testcontainers/keycloak/__init__.py b/src/testcontainers/community/keycloak/__init__.py similarity index 100% rename from modules/keycloak/testcontainers/keycloak/__init__.py rename to src/testcontainers/community/keycloak/__init__.py diff --git a/modules/localstack/testcontainers/localstack/__init__.py b/src/testcontainers/community/localstack/__init__.py similarity index 100% rename from modules/localstack/testcontainers/localstack/__init__.py rename to src/testcontainers/community/localstack/__init__.py diff --git a/modules/mailpit/testcontainers/mailpit/__init__.py b/src/testcontainers/community/mailpit/__init__.py similarity index 100% rename from modules/mailpit/testcontainers/mailpit/__init__.py rename to src/testcontainers/community/mailpit/__init__.py diff --git a/modules/postgres/testcontainers/postgres/py.typed b/src/testcontainers/community/mailpit/py.typed similarity index 100% rename from modules/postgres/testcontainers/postgres/py.typed rename to src/testcontainers/community/mailpit/py.typed diff --git a/modules/memcached/testcontainers/memcached/__init__.py b/src/testcontainers/community/memcached/__init__.py similarity index 100% rename from modules/memcached/testcontainers/memcached/__init__.py rename to src/testcontainers/community/memcached/__init__.py diff --git a/modules/milvus/testcontainers/milvus/__init__.py b/src/testcontainers/community/milvus/__init__.py similarity index 100% rename from modules/milvus/testcontainers/milvus/__init__.py rename to src/testcontainers/community/milvus/__init__.py diff --git a/modules/minio/testcontainers/minio/__init__.py b/src/testcontainers/community/minio/__init__.py similarity index 100% rename from modules/minio/testcontainers/minio/__init__.py rename to src/testcontainers/community/minio/__init__.py diff --git a/modules/mongodb/testcontainers/mongodb/__init__.py b/src/testcontainers/community/mongodb/__init__.py similarity index 100% rename from modules/mongodb/testcontainers/mongodb/__init__.py rename to src/testcontainers/community/mongodb/__init__.py diff --git a/modules/mqtt/testcontainers/mqtt/__init__.py b/src/testcontainers/community/mqtt/__init__.py similarity index 100% rename from modules/mqtt/testcontainers/mqtt/__init__.py rename to src/testcontainers/community/mqtt/__init__.py diff --git a/modules/mqtt/testcontainers/mqtt/testcontainers-mosquitto-default-configuration.conf b/src/testcontainers/community/mqtt/testcontainers-mosquitto-default-configuration.conf similarity index 100% rename from modules/mqtt/testcontainers/mqtt/testcontainers-mosquitto-default-configuration.conf rename to src/testcontainers/community/mqtt/testcontainers-mosquitto-default-configuration.conf diff --git a/modules/mssql/testcontainers/mssql/__init__.py b/src/testcontainers/community/mssql/__init__.py similarity index 100% rename from modules/mssql/testcontainers/mssql/__init__.py rename to src/testcontainers/community/mssql/__init__.py diff --git a/modules/mysql/testcontainers/mysql/__init__.py b/src/testcontainers/community/mysql/__init__.py similarity index 100% rename from modules/mysql/testcontainers/mysql/__init__.py rename to src/testcontainers/community/mysql/__init__.py diff --git a/modules/nats/testcontainers/nats/__init__.py b/src/testcontainers/community/nats/__init__.py similarity index 100% rename from modules/nats/testcontainers/nats/__init__.py rename to src/testcontainers/community/nats/__init__.py diff --git a/modules/neo4j/testcontainers/neo4j/__init__.py b/src/testcontainers/community/neo4j/__init__.py similarity index 100% rename from modules/neo4j/testcontainers/neo4j/__init__.py rename to src/testcontainers/community/neo4j/__init__.py diff --git a/modules/nginx/testcontainers/nginx/__init__.py b/src/testcontainers/community/nginx/__init__.py similarity index 100% rename from modules/nginx/testcontainers/nginx/__init__.py rename to src/testcontainers/community/nginx/__init__.py diff --git a/modules/ollama/testcontainers/ollama/__init__.py b/src/testcontainers/community/ollama/__init__.py similarity index 100% rename from modules/ollama/testcontainers/ollama/__init__.py rename to src/testcontainers/community/ollama/__init__.py diff --git a/modules/openfga/testcontainers/openfga/__init__.py b/src/testcontainers/community/openfga/__init__.py similarity index 100% rename from modules/openfga/testcontainers/openfga/__init__.py rename to src/testcontainers/community/openfga/__init__.py diff --git a/modules/opensearch/testcontainers/opensearch/__init__.py b/src/testcontainers/community/opensearch/__init__.py similarity index 100% rename from modules/opensearch/testcontainers/opensearch/__init__.py rename to src/testcontainers/community/opensearch/__init__.py diff --git a/modules/oracle-free/testcontainers/oracle/__init__.py b/src/testcontainers/community/oracle/__init__.py similarity index 100% rename from modules/oracle-free/testcontainers/oracle/__init__.py rename to src/testcontainers/community/oracle/__init__.py diff --git a/modules/postgres/testcontainers/postgres/__init__.py b/src/testcontainers/community/postgres/__init__.py similarity index 100% rename from modules/postgres/testcontainers/postgres/__init__.py rename to src/testcontainers/community/postgres/__init__.py diff --git a/modules/sftp/testcontainers/sftp/py.typed b/src/testcontainers/community/postgres/py.typed similarity index 100% rename from modules/sftp/testcontainers/sftp/py.typed rename to src/testcontainers/community/postgres/py.typed diff --git a/modules/qdrant/testcontainers/qdrant/__init__.py b/src/testcontainers/community/qdrant/__init__.py similarity index 100% rename from modules/qdrant/testcontainers/qdrant/__init__.py rename to src/testcontainers/community/qdrant/__init__.py diff --git a/modules/rabbitmq/testcontainers/rabbitmq/__init__.py b/src/testcontainers/community/rabbitmq/__init__.py similarity index 100% rename from modules/rabbitmq/testcontainers/rabbitmq/__init__.py rename to src/testcontainers/community/rabbitmq/__init__.py diff --git a/modules/redis/testcontainers/redis/__init__.py b/src/testcontainers/community/redis/__init__.py similarity index 100% rename from modules/redis/testcontainers/redis/__init__.py rename to src/testcontainers/community/redis/__init__.py diff --git a/modules/registry/testcontainers/registry/__init__.py b/src/testcontainers/community/registry/__init__.py similarity index 100% rename from modules/registry/testcontainers/registry/__init__.py rename to src/testcontainers/community/registry/__init__.py diff --git a/modules/scylla/testcontainers/scylla/__init__.py b/src/testcontainers/community/scylla/__init__.py similarity index 100% rename from modules/scylla/testcontainers/scylla/__init__.py rename to src/testcontainers/community/scylla/__init__.py diff --git a/modules/selenium/testcontainers/selenium/__init__.py b/src/testcontainers/community/selenium/__init__.py similarity index 100% rename from modules/selenium/testcontainers/selenium/__init__.py rename to src/testcontainers/community/selenium/__init__.py diff --git a/modules/selenium/testcontainers/selenium/video.py b/src/testcontainers/community/selenium/video.py similarity index 100% rename from modules/selenium/testcontainers/selenium/video.py rename to src/testcontainers/community/selenium/video.py diff --git a/modules/sftp/testcontainers/sftp/__init__.py b/src/testcontainers/community/sftp/__init__.py similarity index 100% rename from modules/sftp/testcontainers/sftp/__init__.py rename to src/testcontainers/community/sftp/__init__.py diff --git a/modules/generic/tests/samples/fastapi/app/__init__.py b/src/testcontainers/community/sftp/py.typed similarity index 100% rename from modules/generic/tests/samples/fastapi/app/__init__.py rename to src/testcontainers/community/sftp/py.typed diff --git a/modules/trino/testcontainers/trino/__init__.py b/src/testcontainers/community/trino/__init__.py similarity index 100% rename from modules/trino/testcontainers/trino/__init__.py rename to src/testcontainers/community/trino/__init__.py diff --git a/modules/valkey/testcontainers/valkey/__init__.py b/src/testcontainers/community/valkey/__init__.py similarity index 100% rename from modules/valkey/testcontainers/valkey/__init__.py rename to src/testcontainers/community/valkey/__init__.py diff --git a/modules/vault/testcontainers/vault/__init__.py b/src/testcontainers/community/vault/__init__.py similarity index 100% rename from modules/vault/testcontainers/vault/__init__.py rename to src/testcontainers/community/vault/__init__.py diff --git a/modules/weaviate/testcontainers/weaviate/__init__.py b/src/testcontainers/community/weaviate/__init__.py similarity index 100% rename from modules/weaviate/testcontainers/weaviate/__init__.py rename to src/testcontainers/community/weaviate/__init__.py diff --git a/core/testcontainers/compose/__init__.py b/src/testcontainers/compose/__init__.py similarity index 100% rename from core/testcontainers/compose/__init__.py rename to src/testcontainers/compose/__init__.py diff --git a/core/testcontainers/compose/compose.py b/src/testcontainers/compose/compose.py similarity index 100% rename from core/testcontainers/compose/compose.py rename to src/testcontainers/compose/compose.py diff --git a/core/testcontainers/core/__init__.py b/src/testcontainers/core/__init__.py similarity index 100% rename from core/testcontainers/core/__init__.py rename to src/testcontainers/core/__init__.py diff --git a/core/testcontainers/core/auth.py b/src/testcontainers/core/auth.py similarity index 100% rename from core/testcontainers/core/auth.py rename to src/testcontainers/core/auth.py diff --git a/core/testcontainers/core/config.py b/src/testcontainers/core/config.py similarity index 100% rename from core/testcontainers/core/config.py rename to src/testcontainers/core/config.py diff --git a/core/testcontainers/core/container.py b/src/testcontainers/core/container.py similarity index 100% rename from core/testcontainers/core/container.py rename to src/testcontainers/core/container.py diff --git a/core/testcontainers/core/docker_client.py b/src/testcontainers/core/docker_client.py similarity index 100% rename from core/testcontainers/core/docker_client.py rename to src/testcontainers/core/docker_client.py diff --git a/core/testcontainers/core/exceptions.py b/src/testcontainers/core/exceptions.py similarity index 100% rename from core/testcontainers/core/exceptions.py rename to src/testcontainers/core/exceptions.py diff --git a/core/testcontainers/core/generic.py b/src/testcontainers/core/generic.py similarity index 100% rename from core/testcontainers/core/generic.py rename to src/testcontainers/core/generic.py diff --git a/core/testcontainers/core/image.py b/src/testcontainers/core/image.py similarity index 100% rename from core/testcontainers/core/image.py rename to src/testcontainers/core/image.py diff --git a/core/testcontainers/core/inspect.py b/src/testcontainers/core/inspect.py similarity index 100% rename from core/testcontainers/core/inspect.py rename to src/testcontainers/core/inspect.py diff --git a/core/testcontainers/core/labels.py b/src/testcontainers/core/labels.py similarity index 100% rename from core/testcontainers/core/labels.py rename to src/testcontainers/core/labels.py diff --git a/core/testcontainers/core/network.py b/src/testcontainers/core/network.py similarity index 100% rename from core/testcontainers/core/network.py rename to src/testcontainers/core/network.py diff --git a/core/testcontainers/core/transferable.py b/src/testcontainers/core/transferable.py similarity index 100% rename from core/testcontainers/core/transferable.py rename to src/testcontainers/core/transferable.py diff --git a/core/testcontainers/core/utils.py b/src/testcontainers/core/utils.py similarity index 100% rename from core/testcontainers/core/utils.py rename to src/testcontainers/core/utils.py diff --git a/core/testcontainers/core/version.py b/src/testcontainers/core/version.py similarity index 100% rename from core/testcontainers/core/version.py rename to src/testcontainers/core/version.py diff --git a/core/testcontainers/core/wait_strategies.py b/src/testcontainers/core/wait_strategies.py similarity index 100% rename from core/testcontainers/core/wait_strategies.py rename to src/testcontainers/core/wait_strategies.py diff --git a/core/testcontainers/core/waiting_utils.py b/src/testcontainers/core/waiting_utils.py similarity index 100% rename from core/testcontainers/core/waiting_utils.py rename to src/testcontainers/core/waiting_utils.py diff --git a/core/testcontainers/socat/__init__.py b/src/testcontainers/socat/__init__.py similarity index 100% rename from core/testcontainers/socat/__init__.py rename to src/testcontainers/socat/__init__.py diff --git a/core/testcontainers/socat/socat.py b/src/testcontainers/socat/socat.py similarity index 100% rename from core/testcontainers/socat/socat.py rename to src/testcontainers/socat/socat.py diff --git a/modules/influxdb/tests/__init__.py b/tests/community/__init__.py similarity index 100% rename from modules/influxdb/tests/__init__.py rename to tests/community/__init__.py diff --git a/modules/arangodb/tests/test_arangodb.py b/tests/community/arangodb/test_arangodb.py similarity index 100% rename from modules/arangodb/tests/test_arangodb.py rename to tests/community/arangodb/test_arangodb.py diff --git a/modules/aws/tests/lambda_sample/Dockerfile b/tests/community/aws/lambda_sample/Dockerfile similarity index 100% rename from modules/aws/tests/lambda_sample/Dockerfile rename to tests/community/aws/lambda_sample/Dockerfile diff --git a/modules/aws/tests/lambda_sample/lambda_function.py b/tests/community/aws/lambda_sample/lambda_function.py similarity index 100% rename from modules/aws/tests/lambda_sample/lambda_function.py rename to tests/community/aws/lambda_sample/lambda_function.py diff --git a/modules/aws/tests/test_aws.py b/tests/community/aws/test_aws.py similarity index 100% rename from modules/aws/tests/test_aws.py rename to tests/community/aws/test_aws.py diff --git a/modules/azurite/tests/samples/network_container/Dockerfile b/tests/community/azurite/samples/network_container/Dockerfile similarity index 100% rename from modules/azurite/tests/samples/network_container/Dockerfile rename to tests/community/azurite/samples/network_container/Dockerfile diff --git a/modules/azurite/tests/samples/network_container/netowrk_container.py b/tests/community/azurite/samples/network_container/netowrk_container.py similarity index 100% rename from modules/azurite/tests/samples/network_container/netowrk_container.py rename to tests/community/azurite/samples/network_container/netowrk_container.py diff --git a/modules/azurite/tests/test_azurite.py b/tests/community/azurite/test_azurite.py similarity index 100% rename from modules/azurite/tests/test_azurite.py rename to tests/community/azurite/test_azurite.py diff --git a/modules/cassandra/tests/test_cassandra.py b/tests/community/cassandra/test_cassandra.py similarity index 100% rename from modules/cassandra/tests/test_cassandra.py rename to tests/community/cassandra/test_cassandra.py diff --git a/modules/chroma/tests/test_chroma.py b/tests/community/chroma/test_chroma.py similarity index 100% rename from modules/chroma/tests/test_chroma.py rename to tests/community/chroma/test_chroma.py diff --git a/modules/clickhouse/tests/test_clickhouse.py b/tests/community/clickhouse/test_clickhouse.py similarity index 100% rename from modules/clickhouse/tests/test_clickhouse.py rename to tests/community/clickhouse/test_clickhouse.py diff --git a/modules/cockroachdb/tests/test_cockroachdb.py b/tests/community/cockroachdb/test_cockroachdb.py similarity index 100% rename from modules/cockroachdb/tests/test_cockroachdb.py rename to tests/community/cockroachdb/test_cockroachdb.py diff --git a/modules/cosmosdb/tests/test_cosmosdb_emulator.py b/tests/community/cosmosdb/test_cosmosdb_emulator.py similarity index 100% rename from modules/cosmosdb/tests/test_cosmosdb_emulator.py rename to tests/community/cosmosdb/test_cosmosdb_emulator.py diff --git a/modules/cosmosdb/tests/test_cosmosdb_mongodb.py b/tests/community/cosmosdb/test_cosmosdb_mongodb.py similarity index 100% rename from modules/cosmosdb/tests/test_cosmosdb_mongodb.py rename to tests/community/cosmosdb/test_cosmosdb_mongodb.py diff --git a/modules/cosmosdb/tests/test_cosmosdb_nosql.py b/tests/community/cosmosdb/test_cosmosdb_nosql.py similarity index 100% rename from modules/cosmosdb/tests/test_cosmosdb_nosql.py rename to tests/community/cosmosdb/test_cosmosdb_nosql.py diff --git a/modules/db2/tests/test_db2.py b/tests/community/db2/test_db2.py similarity index 100% rename from modules/db2/tests/test_db2.py rename to tests/community/db2/test_db2.py diff --git a/modules/elasticsearch/tests/test_elasticsearch.py b/tests/community/elasticsearch/test_elasticsearch.py similarity index 100% rename from modules/elasticsearch/tests/test_elasticsearch.py rename to tests/community/elasticsearch/test_elasticsearch.py diff --git a/modules/generic/tests/conftest.py b/tests/community/generic/conftest.py similarity index 100% rename from modules/generic/tests/conftest.py rename to tests/community/generic/conftest.py diff --git a/modules/generic/tests/samples/advance_1/Dockerfile b/tests/community/generic/samples/advance_1/Dockerfile similarity index 100% rename from modules/generic/tests/samples/advance_1/Dockerfile rename to tests/community/generic/samples/advance_1/Dockerfile diff --git a/modules/opensearch/example_basic.py b/tests/community/generic/samples/advance_1/app/__init__.py similarity index 100% rename from modules/opensearch/example_basic.py rename to tests/community/generic/samples/advance_1/app/__init__.py diff --git a/modules/generic/tests/samples/advance_1/app/main.py b/tests/community/generic/samples/advance_1/app/main.py similarity index 100% rename from modules/generic/tests/samples/advance_1/app/main.py rename to tests/community/generic/samples/advance_1/app/main.py diff --git a/modules/generic/tests/samples/fastapi/Dockerfile b/tests/community/generic/samples/fastapi/Dockerfile similarity index 100% rename from modules/generic/tests/samples/fastapi/Dockerfile rename to tests/community/generic/samples/fastapi/Dockerfile diff --git a/tests/community/generic/samples/fastapi/app/__init__.py b/tests/community/generic/samples/fastapi/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/generic/tests/samples/fastapi/app/main.py b/tests/community/generic/samples/fastapi/app/main.py similarity index 100% rename from modules/generic/tests/samples/fastapi/app/main.py rename to tests/community/generic/samples/fastapi/app/main.py diff --git a/modules/generic/tests/samples/python_server/Dockerfile b/tests/community/generic/samples/python_server/Dockerfile similarity index 100% rename from modules/generic/tests/samples/python_server/Dockerfile rename to tests/community/generic/samples/python_server/Dockerfile diff --git a/modules/generic/tests/test_server.py b/tests/community/generic/test_server.py similarity index 100% rename from modules/generic/tests/test_server.py rename to tests/community/generic/test_server.py diff --git a/modules/generic/tests/test_sql.py b/tests/community/generic/test_sql.py similarity index 100% rename from modules/generic/tests/test_sql.py rename to tests/community/generic/test_sql.py diff --git a/modules/google/tests/test_google.py b/tests/community/google/test_google.py similarity index 100% rename from modules/google/tests/test_google.py rename to tests/community/google/test_google.py diff --git a/tests/community/influxdb/__init__.py b/tests/community/influxdb/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/influxdb/tests/test_influxdb.py b/tests/community/influxdb/test_influxdb.py similarity index 100% rename from modules/influxdb/tests/test_influxdb.py rename to tests/community/influxdb/test_influxdb.py diff --git a/modules/k3s/tests/test_k3s.py b/tests/community/k3s/test_k3s.py similarity index 100% rename from modules/k3s/tests/test_k3s.py rename to tests/community/k3s/test_k3s.py diff --git a/modules/kafka/tests/test_kafka.py b/tests/community/kafka/test_kafka.py similarity index 100% rename from modules/kafka/tests/test_kafka.py rename to tests/community/kafka/test_kafka.py diff --git a/modules/kafka/tests/test_redpanda.py b/tests/community/kafka/test_redpanda.py similarity index 100% rename from modules/kafka/tests/test_redpanda.py rename to tests/community/kafka/test_redpanda.py diff --git a/modules/keycloak/tests/test_keycloak.py b/tests/community/keycloak/test_keycloak.py similarity index 100% rename from modules/keycloak/tests/test_keycloak.py rename to tests/community/keycloak/test_keycloak.py diff --git a/modules/localstack/tests/test_localstack.py b/tests/community/localstack/test_localstack.py similarity index 100% rename from modules/localstack/tests/test_localstack.py rename to tests/community/localstack/test_localstack.py diff --git a/modules/mailpit/tests/test_mailpit.py b/tests/community/mailpit/test_mailpit.py similarity index 100% rename from modules/mailpit/tests/test_mailpit.py rename to tests/community/mailpit/test_mailpit.py diff --git a/modules/memcached/tests/test_memcached.py b/tests/community/memcached/test_memcached.py similarity index 100% rename from modules/memcached/tests/test_memcached.py rename to tests/community/memcached/test_memcached.py diff --git a/modules/milvus/tests/test_milvus.py b/tests/community/milvus/test_milvus.py similarity index 100% rename from modules/milvus/tests/test_milvus.py rename to tests/community/milvus/test_milvus.py diff --git a/modules/minio/tests/test_minio.py b/tests/community/minio/test_minio.py similarity index 100% rename from modules/minio/tests/test_minio.py rename to tests/community/minio/test_minio.py diff --git a/modules/mongodb/tests/test_mongodb.py b/tests/community/mongodb/test_mongodb.py similarity index 100% rename from modules/mongodb/tests/test_mongodb.py rename to tests/community/mongodb/test_mongodb.py diff --git a/modules/mqtt/tests/test_mosquitto.py b/tests/community/mqtt/test_mosquitto.py similarity index 100% rename from modules/mqtt/tests/test_mosquitto.py rename to tests/community/mqtt/test_mosquitto.py diff --git a/modules/mssql/tests/test_mssql.py b/tests/community/mssql/test_mssql.py similarity index 100% rename from modules/mssql/tests/test_mssql.py rename to tests/community/mssql/test_mssql.py diff --git a/modules/mysql/tests/seeds/01-schema.sql b/tests/community/mysql/seeds/01-schema.sql similarity index 100% rename from modules/mysql/tests/seeds/01-schema.sql rename to tests/community/mysql/seeds/01-schema.sql diff --git a/modules/mysql/tests/seeds/02-seeds.sql b/tests/community/mysql/seeds/02-seeds.sql similarity index 100% rename from modules/mysql/tests/seeds/02-seeds.sql rename to tests/community/mysql/seeds/02-seeds.sql diff --git a/modules/mysql/tests/test_mysql.py b/tests/community/mysql/test_mysql.py similarity index 100% rename from modules/mysql/tests/test_mysql.py rename to tests/community/mysql/test_mysql.py diff --git a/modules/nats/tests/test_nats.py b/tests/community/nats/test_nats.py similarity index 100% rename from modules/nats/tests/test_nats.py rename to tests/community/nats/test_nats.py diff --git a/modules/nats/tests/test_nats_jetstream.py b/tests/community/nats/test_nats_jetstream.py similarity index 100% rename from modules/nats/tests/test_nats_jetstream.py rename to tests/community/nats/test_nats_jetstream.py diff --git a/modules/neo4j/tests/test_neo4j.py b/tests/community/neo4j/test_neo4j.py similarity index 100% rename from modules/neo4j/tests/test_neo4j.py rename to tests/community/neo4j/test_neo4j.py diff --git a/modules/nginx/tests/test_nginx.py b/tests/community/nginx/test_nginx.py similarity index 100% rename from modules/nginx/tests/test_nginx.py rename to tests/community/nginx/test_nginx.py diff --git a/modules/ollama/tests/test_ollama.py b/tests/community/ollama/test_ollama.py similarity index 100% rename from modules/ollama/tests/test_ollama.py rename to tests/community/ollama/test_ollama.py diff --git a/modules/openfga/tests/test_openfga.py b/tests/community/openfga/test_openfga.py similarity index 100% rename from modules/openfga/tests/test_openfga.py rename to tests/community/openfga/test_openfga.py diff --git a/modules/opensearch/tests/test_opensearch.py b/tests/community/opensearch/test_opensearch.py similarity index 100% rename from modules/opensearch/tests/test_opensearch.py rename to tests/community/opensearch/test_opensearch.py diff --git a/modules/oracle-free/tests/test_oracle.py b/tests/community/oracle-free/test_oracle.py similarity index 100% rename from modules/oracle-free/tests/test_oracle.py rename to tests/community/oracle-free/test_oracle.py diff --git a/modules/postgres/tests/fixtures/postgres_create_example_table.sql b/tests/community/postgres/fixtures/postgres_create_example_table.sql similarity index 100% rename from modules/postgres/tests/fixtures/postgres_create_example_table.sql rename to tests/community/postgres/fixtures/postgres_create_example_table.sql diff --git a/modules/postgres/tests/test_postgres.py b/tests/community/postgres/test_postgres.py similarity index 100% rename from modules/postgres/tests/test_postgres.py rename to tests/community/postgres/test_postgres.py diff --git a/modules/qdrant/tests/test_config.yaml b/tests/community/qdrant/test_config.yaml similarity index 100% rename from modules/qdrant/tests/test_config.yaml rename to tests/community/qdrant/test_config.yaml diff --git a/modules/qdrant/tests/test_qdrant.py b/tests/community/qdrant/test_qdrant.py similarity index 100% rename from modules/qdrant/tests/test_qdrant.py rename to tests/community/qdrant/test_qdrant.py diff --git a/modules/rabbitmq/tests/test_rabbitmq.py b/tests/community/rabbitmq/test_rabbitmq.py similarity index 100% rename from modules/rabbitmq/tests/test_rabbitmq.py rename to tests/community/rabbitmq/test_rabbitmq.py diff --git a/modules/redis/tests/test_redis.py b/tests/community/redis/test_redis.py similarity index 100% rename from modules/redis/tests/test_redis.py rename to tests/community/redis/test_redis.py diff --git a/modules/registry/tests/test_registry.py b/tests/community/registry/test_registry.py similarity index 100% rename from modules/registry/tests/test_registry.py rename to tests/community/registry/test_registry.py diff --git a/modules/scylla/tests/test_scylla.py b/tests/community/scylla/test_scylla.py similarity index 100% rename from modules/scylla/tests/test_scylla.py rename to tests/community/scylla/test_scylla.py diff --git a/modules/selenium/tests/test_selenium.py b/tests/community/selenium/test_selenium.py similarity index 100% rename from modules/selenium/tests/test_selenium.py rename to tests/community/selenium/test_selenium.py diff --git a/modules/sftp/tests/test_sftp.py b/tests/community/sftp/test_sftp.py similarity index 100% rename from modules/sftp/tests/test_sftp.py rename to tests/community/sftp/test_sftp.py diff --git a/modules/trino/tests/test_trino.py b/tests/community/trino/test_trino.py similarity index 100% rename from modules/trino/tests/test_trino.py rename to tests/community/trino/test_trino.py diff --git a/modules/valkey/tests/test_valkey.py b/tests/community/valkey/test_valkey.py similarity index 100% rename from modules/valkey/tests/test_valkey.py rename to tests/community/valkey/test_valkey.py diff --git a/modules/vault/tests/test_vault.py b/tests/community/vault/test_vault.py similarity index 100% rename from modules/vault/tests/test_vault.py rename to tests/community/vault/test_vault.py diff --git a/modules/weaviate/tests/test_weaviate.py b/tests/community/weaviate/test_weaviate.py similarity index 100% rename from modules/weaviate/tests/test_weaviate.py rename to tests/community/weaviate/test_weaviate.py diff --git a/tests/core/__init__.py b/tests/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/core/tests/_local_registry_container.py b/tests/core/_local_registry_container.py similarity index 100% rename from core/tests/_local_registry_container.py rename to tests/core/_local_registry_container.py diff --git a/core/tests/compose_fixtures/basic/docker-compose.yaml b/tests/core/compose_fixtures/basic/docker-compose.yaml similarity index 100% rename from core/tests/compose_fixtures/basic/docker-compose.yaml rename to tests/core/compose_fixtures/basic/docker-compose.yaml diff --git a/core/tests/compose_fixtures/basic/hello.yaml b/tests/core/compose_fixtures/basic/hello.yaml similarity index 100% rename from core/tests/compose_fixtures/basic/hello.yaml rename to tests/core/compose_fixtures/basic/hello.yaml diff --git a/core/tests/compose_fixtures/basic_multiple/docker-compose.yaml b/tests/core/compose_fixtures/basic_multiple/docker-compose.yaml similarity index 100% rename from core/tests/compose_fixtures/basic_multiple/docker-compose.yaml rename to tests/core/compose_fixtures/basic_multiple/docker-compose.yaml diff --git a/core/tests/compose_fixtures/basic_volume/docker-compose.yaml b/tests/core/compose_fixtures/basic_volume/docker-compose.yaml similarity index 100% rename from core/tests/compose_fixtures/basic_volume/docker-compose.yaml rename to tests/core/compose_fixtures/basic_volume/docker-compose.yaml diff --git a/core/tests/compose_fixtures/port_multiple/compose.yaml b/tests/core/compose_fixtures/port_multiple/compose.yaml similarity index 100% rename from core/tests/compose_fixtures/port_multiple/compose.yaml rename to tests/core/compose_fixtures/port_multiple/compose.yaml diff --git a/core/tests/compose_fixtures/port_single/compose.yaml b/tests/core/compose_fixtures/port_single/compose.yaml similarity index 100% rename from core/tests/compose_fixtures/port_single/compose.yaml rename to tests/core/compose_fixtures/port_single/compose.yaml diff --git a/core/tests/compose_fixtures/profile_support/compose.yaml b/tests/core/compose_fixtures/profile_support/compose.yaml similarity index 100% rename from core/tests/compose_fixtures/profile_support/compose.yaml rename to tests/core/compose_fixtures/profile_support/compose.yaml diff --git a/core/tests/conftest.py b/tests/core/conftest.py similarity index 100% rename from core/tests/conftest.py rename to tests/core/conftest.py diff --git a/core/tests/image_fixtures/busybox/Dockerfile b/tests/core/image_fixtures/busybox/Dockerfile similarity index 100% rename from core/tests/image_fixtures/busybox/Dockerfile rename to tests/core/image_fixtures/busybox/Dockerfile diff --git a/core/tests/image_fixtures/sample/Dockerfile b/tests/core/image_fixtures/sample/Dockerfile similarity index 100% rename from core/tests/image_fixtures/sample/Dockerfile rename to tests/core/image_fixtures/sample/Dockerfile diff --git a/core/tests/test_auth.py b/tests/core/test_auth.py similarity index 100% rename from core/tests/test_auth.py rename to tests/core/test_auth.py diff --git a/core/tests/test_compose.py b/tests/core/test_compose.py similarity index 100% rename from core/tests/test_compose.py rename to tests/core/test_compose.py diff --git a/core/tests/test_config.py b/tests/core/test_config.py similarity index 100% rename from core/tests/test_config.py rename to tests/core/test_config.py diff --git a/core/tests/test_container.py b/tests/core/test_container.py similarity index 100% rename from core/tests/test_container.py rename to tests/core/test_container.py diff --git a/core/tests/test_core.py b/tests/core/test_core.py similarity index 100% rename from core/tests/test_core.py rename to tests/core/test_core.py diff --git a/core/tests/test_core_ports.py b/tests/core/test_core_ports.py similarity index 100% rename from core/tests/test_core_ports.py rename to tests/core/test_core_ports.py diff --git a/core/tests/test_core_registry.py b/tests/core/test_core_registry.py similarity index 100% rename from core/tests/test_core_registry.py rename to tests/core/test_core_registry.py diff --git a/core/tests/test_docker_client.py b/tests/core/test_docker_client.py similarity index 100% rename from core/tests/test_docker_client.py rename to tests/core/test_docker_client.py diff --git a/core/tests/test_docker_in_docker.py b/tests/core/test_docker_in_docker.py similarity index 100% rename from core/tests/test_docker_in_docker.py rename to tests/core/test_docker_in_docker.py diff --git a/core/tests/test_image.py b/tests/core/test_image.py similarity index 100% rename from core/tests/test_image.py rename to tests/core/test_image.py diff --git a/core/tests/test_inspect.py b/tests/core/test_inspect.py similarity index 100% rename from core/tests/test_inspect.py rename to tests/core/test_inspect.py diff --git a/core/tests/test_labels.py b/tests/core/test_labels.py similarity index 100% rename from core/tests/test_labels.py rename to tests/core/test_labels.py diff --git a/core/tests/test_network.py b/tests/core/test_network.py similarity index 100% rename from core/tests/test_network.py rename to tests/core/test_network.py diff --git a/core/tests/test_new_docker_api.py b/tests/core/test_new_docker_api.py similarity index 100% rename from core/tests/test_new_docker_api.py rename to tests/core/test_new_docker_api.py diff --git a/core/tests/test_protocol_compliance.py b/tests/core/test_protocol_compliance.py similarity index 100% rename from core/tests/test_protocol_compliance.py rename to tests/core/test_protocol_compliance.py diff --git a/core/tests/test_ryuk.py b/tests/core/test_ryuk.py similarity index 100% rename from core/tests/test_ryuk.py rename to tests/core/test_ryuk.py diff --git a/core/tests/test_socat.py b/tests/core/test_socat.py similarity index 100% rename from core/tests/test_socat.py rename to tests/core/test_socat.py diff --git a/core/tests/test_transferable.py b/tests/core/test_transferable.py similarity index 100% rename from core/tests/test_transferable.py rename to tests/core/test_transferable.py diff --git a/core/tests/test_utils.py b/tests/core/test_utils.py similarity index 100% rename from core/tests/test_utils.py rename to tests/core/test_utils.py diff --git a/core/tests/test_version.py b/tests/core/test_version.py similarity index 100% rename from core/tests/test_version.py rename to tests/core/test_version.py diff --git a/core/tests/test_wait_strategies.py b/tests/core/test_wait_strategies.py similarity index 100% rename from core/tests/test_wait_strategies.py rename to tests/core/test_wait_strategies.py diff --git a/core/tests/test_wait_strategies_integration.py b/tests/core/test_wait_strategies_integration.py similarity index 100% rename from core/tests/test_wait_strategies_integration.py rename to tests/core/test_wait_strategies_integration.py diff --git a/core/tests/test_waiting_utils.py b/tests/core/test_waiting_utils.py similarity index 100% rename from core/tests/test_waiting_utils.py rename to tests/core/test_waiting_utils.py From c0afb680be0291ebc559fb39b4195239b0cc14a5 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 00:40:37 +0200 Subject: [PATCH 02/29] =?UTF-8?q?chore(main):=20fix=20imports=20after=20te?= =?UTF-8?q?stcontainer.=20=E2=86=92=20testcontainers.community.=20move?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/community/arangodb.rst | 4 ++-- docs/community/aws.rst | 4 ++-- docs/community/azurite.rst | 4 ++-- docs/community/cassandra.rst | 4 ++-- docs/community/chroma.rst | 4 ++-- docs/community/clickhouse.rst | 4 ++-- docs/community/cockroachdb.rst | 4 ++-- docs/community/cosmosdb.rst | 8 ++++---- docs/community/db2.rst | 4 ++-- docs/community/elasticsearch.rst | 4 ++-- docs/community/generic.rst | 18 +++++++++--------- docs/community/google.rst | 8 ++++---- docs/community/influxdb.rst | 4 ++-- docs/community/k3s.rst | 4 ++-- docs/community/kafka.rst | 6 +++--- docs/community/keycloak.rst | 4 ++-- docs/community/localstack.rst | 4 ++-- docs/community/mailpit.rst | 6 +++--- docs/community/memcached.rst | 2 +- docs/community/milvus.rst | 4 ++-- docs/community/minio.rst | 4 ++-- docs/community/mongodb.rst | 6 +++--- docs/community/mqtt.rst | 4 ++-- docs/community/mssql.rst | 4 ++-- docs/community/mysql.rst | 4 ++-- docs/community/nats.rst | 4 ++-- docs/community/neo4j.rst | 4 ++-- docs/community/nginx.rst | 4 ++-- docs/community/ollama.rst | 4 ++-- docs/community/openfga.rst | 4 ++-- docs/community/opensearch.rst | 4 ++-- docs/community/oracle-free.rst | 4 ++-- docs/community/postgres.rst | 4 ++-- docs/community/qdrant.rst | 4 ++-- docs/community/rabbitmq.rst | 4 ++-- docs/community/redis.rst | 4 ++-- docs/community/registry.rst | 2 +- docs/community/scylla.rst | 4 ++-- docs/community/selenium.rst | 4 ++-- docs/community/sftp.rst | 6 +++--- docs/community/trino.rst | 4 ++-- docs/community/valkey.rst | 4 ++-- docs/community/vault.rst | 4 ++-- docs/community/weaviate.rst | 4 ++-- docs/features/authentication.md | 8 ++++---- docs/features/container_logs.md | 4 ++-- docs/features/copying_data.md | 2 +- docs/features/creating_container.md | 6 +++--- docs/features/executing_commands.md | 4 ++-- docs/features/networking.md | 4 ++-- docs/features/wait_strategies.md | 4 ++-- docs/index.rst | 4 ++-- docs/modules/arangodb_example.py | 2 +- docs/modules/aws_example.py | 4 ++-- docs/modules/azurite_example.py | 2 +- docs/modules/cassandra_example.py | 2 +- docs/modules/chroma_example.py | 2 +- docs/modules/clickhouse_example.py | 2 +- docs/modules/cockroachdb_example.py | 2 +- docs/modules/cosmosdb_example.py | 2 +- docs/modules/db2_example.py | 2 +- docs/modules/elasticsearch_example.py | 2 +- docs/modules/generic_example.py | 2 +- docs/modules/google_example.py | 2 +- docs/modules/influxdb_example.py | 2 +- docs/modules/k3s_example.py | 2 +- docs/modules/kafka_example.py | 2 +- docs/modules/keycloak_example.py | 2 +- docs/modules/localstack_example.py | 2 +- docs/modules/mailpit_example.py | 2 +- docs/modules/memcached_example.py | 2 +- docs/modules/milvus_example.py | 2 +- docs/modules/minio_example.py | 2 +- docs/modules/mongodb_example.py | 2 +- docs/modules/mqtt_example.py | 2 +- docs/modules/mssql_example.py | 2 +- docs/modules/mysql_example.py | 2 +- docs/modules/nats_example.py | 2 +- docs/modules/neo4j_example.py | 2 +- docs/modules/nginx_example.py | 2 +- docs/modules/ollama_example.py | 2 +- docs/modules/postgres_example.py | 2 +- docs/modules/qdrant_example.py | 2 +- docs/modules/rabbitmq_example.py | 2 +- docs/modules/redis_example.py | 2 +- docs/modules/registry_example.py | 2 +- docs/modules/scylla_example.py | 2 +- docs/modules/selenium_example.py | 2 +- docs/modules/sftp_example.py | 2 +- docs/modules/trino_example.py | 2 +- docs/modules/valkey_example.py | 2 +- docs/modules/vault_example.py | 2 +- docs/modules/weaviate_example.py | 2 +- docs/quickstart.md | 2 +- .../community/arangodb/__init__.py | 2 +- src/testcontainers/community/aws/aws_lambda.py | 5 ++--- .../community/azurite/__init__.py | 2 +- .../community/cassandra/__init__.py | 2 +- .../community/chroma/__init__.py | 3 +-- .../community/clickhouse/__init__.py | 2 +- .../community/cockroachdb/__init__.py | 2 +- .../community/cosmosdb/mongodb.py | 2 +- src/testcontainers/community/cosmosdb/nosql.py | 5 ++--- src/testcontainers/community/db2/__init__.py | 2 +- .../community/elasticsearch/__init__.py | 2 +- src/testcontainers/community/generic/server.py | 2 +- .../community/google/datastore.py | 5 +++-- src/testcontainers/community/google/pubsub.py | 5 +++-- .../community/influxdb1/__init__.py | 5 ++--- .../community/influxdb2/__init__.py | 5 ++--- src/testcontainers/community/k3s/__init__.py | 2 +- src/testcontainers/community/kafka/__init__.py | 7 +++---- .../community/kafka/_redpanda.py | 2 +- .../community/keycloak/__init__.py | 6 +++--- .../community/localstack/__init__.py | 3 +-- .../community/mailpit/__init__.py | 7 +++---- .../community/memcached/__init__.py | 2 +- .../community/milvus/__init__.py | 3 +-- src/testcontainers/community/minio/__init__.py | 5 +++-- .../community/mongodb/__init__.py | 10 ++++------ src/testcontainers/community/mqtt/__init__.py | 5 ++--- src/testcontainers/community/mssql/__init__.py | 2 +- src/testcontainers/community/mysql/__init__.py | 4 ++-- src/testcontainers/community/nats/__init__.py | 2 +- src/testcontainers/community/neo4j/__init__.py | 6 ++++-- .../community/ollama/__init__.py | 5 ++--- .../community/openfga/__init__.py | 3 +-- .../community/opensearch/__init__.py | 5 ++--- .../community/oracle/__init__.py | 2 +- .../community/postgres/__init__.py | 2 +- .../community/qdrant/__init__.py | 2 +- .../community/rabbitmq/__init__.py | 3 +-- src/testcontainers/community/redis/__init__.py | 9 +++++---- .../community/scylla/__init__.py | 2 +- .../community/selenium/__init__.py | 10 +++++----- src/testcontainers/community/sftp/__init__.py | 7 +++---- src/testcontainers/community/vault/__init__.py | 2 +- .../community/weaviate/__init__.py | 5 ++--- src/testcontainers/core/generic.py | 2 +- tests/community/generic/test_server.py | 2 +- tests/community/generic/test_sql.py | 6 +++--- 141 files changed, 248 insertions(+), 260 deletions(-) diff --git a/docs/community/arangodb.rst b/docs/community/arangodb.rst index d1a6cd255..4d4781ba1 100644 --- a/docs/community/arangodb.rst +++ b/docs/community/arangodb.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.arangodb.ArangoDbContainer -.. title:: testcontainers.arangodb.ArangoDbContainer +.. autoclass:: testcontainers.community.arangodb.ArangoDbContainer +.. title:: testcontainers.community.arangodb.ArangoDbContainer diff --git a/docs/community/aws.rst b/docs/community/aws.rst index a44dc856f..30e66af4a 100644 --- a/docs/community/aws.rst +++ b/docs/community/aws.rst @@ -1,7 +1,7 @@ :code:`testcontainers-aws` is a set of AWS containers modules that can be used to create AWS containers. -.. autoclass:: testcontainers.aws.AWSLambdaContainer -.. title:: testcontainers.aws.AWSLambdaContainer +.. autoclass:: testcontainers.community.aws.AWSLambdaContainer +.. title:: testcontainers.community.aws.AWSLambdaContainer The following environment variables are used by the AWS Lambda container: diff --git a/docs/community/azurite.rst b/docs/community/azurite.rst index b6cf724c9..e56af0c75 100644 --- a/docs/community/azurite.rst +++ b/docs/community/azurite.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.azurite.AzuriteContainer -.. title:: testcontainers.azurite.AzuriteContainer +.. autoclass:: testcontainers.community.azurite.AzuriteContainer +.. title:: testcontainers.community.azurite.AzuriteContainer diff --git a/docs/community/cassandra.rst b/docs/community/cassandra.rst index 44216d6be..5726f09be 100644 --- a/docs/community/cassandra.rst +++ b/docs/community/cassandra.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.cassandra.CassandraContainer -.. title:: testcontainers.cassandra.CassandraContainer +.. autoclass:: testcontainers.community.cassandra.CassandraContainer +.. title:: testcontainers.community.cassandra.CassandraContainer diff --git a/docs/community/chroma.rst b/docs/community/chroma.rst index f4e3199fe..9b028a3b6 100644 --- a/docs/community/chroma.rst +++ b/docs/community/chroma.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.chroma.ChromaContainer -.. title:: testcontainers.minio.ChromaContainer +.. autoclass:: testcontainers.community.chroma.ChromaContainer +.. title:: testcontainers.community.chroma.ChromaContainer diff --git a/docs/community/clickhouse.rst b/docs/community/clickhouse.rst index 0835d4c04..9840370ab 100644 --- a/docs/community/clickhouse.rst +++ b/docs/community/clickhouse.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.clickhouse.ClickHouseContainer -.. title:: testcontainers.clickhouse.ClickHouseContainer +.. autoclass:: testcontainers.community.clickhouse.ClickHouseContainer +.. title:: testcontainers.community.clickhouse.ClickHouseContainer diff --git a/docs/community/cockroachdb.rst b/docs/community/cockroachdb.rst index 7b53fc336..bdbcfe3a7 100644 --- a/docs/community/cockroachdb.rst +++ b/docs/community/cockroachdb.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.cockroachdb.CockroachDBContainer -.. title:: testcontainers.cockroachdb.CockroachDBContainer +.. autoclass:: testcontainers.community.cockroachdb.CockroachDBContainer +.. title:: testcontainers.community.cockroachdb.CockroachDBContainer diff --git a/docs/community/cosmosdb.rst b/docs/community/cosmosdb.rst index 802cffa4e..ccf6ee631 100644 --- a/docs/community/cosmosdb.rst +++ b/docs/community/cosmosdb.rst @@ -1,5 +1,5 @@ -.. autoclass:: testcontainers.cosmosdb.CosmosDBMongoEndpointContainer -.. title:: testcontainers.cosmosdb.CosmosDBMongoEndpointContainer +.. autoclass:: testcontainers.community.cosmosdb.CosmosDBMongoEndpointContainer +.. title:: testcontainers.community.cosmosdb.CosmosDBMongoEndpointContainer -.. autoclass:: testcontainers.cosmosdb.CosmosDBNoSQLEndpointContainer -.. title:: testcontainers.cosmosdb.CosmosDBNoSQLEndpointContainer +.. autoclass:: testcontainers.community.cosmosdb.CosmosDBNoSQLEndpointContainer +.. title:: testcontainers.community.cosmosdb.CosmosDBNoSQLEndpointContainer diff --git a/docs/community/db2.rst b/docs/community/db2.rst index 1afd1f6d7..494875cc6 100644 --- a/docs/community/db2.rst +++ b/docs/community/db2.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.db2.Db2Container -.. title:: testcontainers.db2.Db2Container +.. autoclass:: testcontainers.community.db2.Db2Container +.. title:: testcontainers.community.db2.Db2Container diff --git a/docs/community/elasticsearch.rst b/docs/community/elasticsearch.rst index c2ce1930f..4d93ab1c4 100644 --- a/docs/community/elasticsearch.rst +++ b/docs/community/elasticsearch.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.elasticsearch.ElasticSearchContainer -.. title:: testcontainers.elasticsearch.ElasticSearchContainer +.. autoclass:: testcontainers.community.elasticsearch.ElasticSearchContainer +.. title:: testcontainers.community.elasticsearch.ElasticSearchContainer diff --git a/docs/community/generic.rst b/docs/community/generic.rst index 4b7281121..5d062b5db 100644 --- a/docs/community/generic.rst +++ b/docs/community/generic.rst @@ -1,13 +1,13 @@ :code:`testcontainers-generic` is a set of generic containers modules that can be used to creat containers. -.. autoclass:: testcontainers.generic.ServerContainer -.. title:: testcontainers.generic.ServerContainer +.. autoclass:: testcontainers.community.generic.ServerContainer +.. title:: testcontainers.community.generic.ServerContainer FastAPI container that is using :code:`ServerContainer` .. doctest:: - >>> from testcontainers.generic import ServerContainer + >>> from testcontainers.community.generic import ServerContainer >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage @@ -24,8 +24,8 @@ A more advance use-case, where we are using a FastAPI container that is using Re .. doctest:: - >>> from testcontainers.redis import RedisContainer - >>> from testcontainers.generic import ServerContainer + >>> from testcontainers.community.redis import RedisContainer + >>> from testcontainers.community.generic import ServerContainer >>> with RedisContainer() as redis: ... redis_container_port = redis.port @@ -52,15 +52,15 @@ A more advance use-case, where we are using a FastAPI container that is using Re ... assert response.status_code == 200, "Failed to get data" ... assert response.json() == {"key": test_data["key"], "value": test_data["value"]} -.. autoclass:: testcontainers.generic.SqlContainer -.. title:: testcontainers.generic.SqlContainer +.. autoclass:: testcontainers.community.generic.SqlContainer +.. title:: testcontainers.community.generic.SqlContainer Postgres container that is using :code:`SqlContainer` .. doctest:: - >>> from testcontainers.generic import SqlContainer - >>> from testcontainers.generic.providers.sql_connection_wait_strategy import SqlAlchemyConnectWaitStrategy + >>> from testcontainers.community.generic import SqlContainer + >>> from testcontainers.community.generic.providers.sql_connection_wait_strategy import SqlAlchemyConnectWaitStrategy >>> from sqlalchemy import text >>> import sqlalchemy diff --git a/docs/community/google.rst b/docs/community/google.rst index 903c3c4a1..761c7fe7d 100644 --- a/docs/community/google.rst +++ b/docs/community/google.rst @@ -1,4 +1,4 @@ -.. autoclass:: testcontainers.google.DatastoreContainer -.. title:: testcontainers.google.DatastoreContainer -.. autoclass:: testcontainers.google.PubSubContainer -.. title:: testcontainers.google.PubSubContainer +.. autoclass:: testcontainers.community.google.DatastoreContainer +.. title:: testcontainers.community.google.DatastoreContainer +.. autoclass:: testcontainers.community.google.PubSubContainer +.. title:: testcontainers.community.google.PubSubContainer diff --git a/docs/community/influxdb.rst b/docs/community/influxdb.rst index 8424ebea3..ff7ba1e30 100644 --- a/docs/community/influxdb.rst +++ b/docs/community/influxdb.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.influxdb.InfluxDbContainer -.. title:: testcontainers.influxdb.InfluxDbContainer +.. autoclass:: testcontainers.community.influxdb.InfluxDbContainer +.. title:: testcontainers.community.influxdb.InfluxDbContainer diff --git a/docs/community/k3s.rst b/docs/community/k3s.rst index 11f7adc05..6e295ee2f 100644 --- a/docs/community/k3s.rst +++ b/docs/community/k3s.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.k3s.K3SContainer -.. title:: testcontainers.k3s.K3SContainer +.. autoclass:: testcontainers.community.k3s.K3SContainer +.. title:: testcontainers.community.k3s.K3SContainer diff --git a/docs/community/kafka.rst b/docs/community/kafka.rst index a54107a02..189df27d2 100644 --- a/docs/community/kafka.rst +++ b/docs/community/kafka.rst @@ -1,3 +1,3 @@ -.. autoclass:: testcontainers.kafka.KafkaContainer -.. title:: testcontainers.kafka.KafkaContainer -.. autoclass:: testcontainers.kafka.RedpandaContainer +.. autoclass:: testcontainers.community.kafka.KafkaContainer +.. title:: testcontainers.community.kafka.KafkaContainer +.. autoclass:: testcontainers.community.kafka.RedpandaContainer diff --git a/docs/community/keycloak.rst b/docs/community/keycloak.rst index 6eb045f4a..8826c8260 100644 --- a/docs/community/keycloak.rst +++ b/docs/community/keycloak.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.keycloak.KeycloakContainer -.. title:: testcontainers.keycloak.KeycloakContainer +.. autoclass:: testcontainers.community.keycloak.KeycloakContainer +.. title:: testcontainers.community.keycloak.KeycloakContainer diff --git a/docs/community/localstack.rst b/docs/community/localstack.rst index 66cbf4d3c..43f11066e 100644 --- a/docs/community/localstack.rst +++ b/docs/community/localstack.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.localstack.LocalStackContainer -.. title:: testcontainers.localstack.LocalStackContainer +.. autoclass:: testcontainers.community.localstack.LocalStackContainer +.. title:: testcontainers.community.localstack.LocalStackContainer diff --git a/docs/community/mailpit.rst b/docs/community/mailpit.rst index f2c238a37..ea5e29391 100644 --- a/docs/community/mailpit.rst +++ b/docs/community/mailpit.rst @@ -1,3 +1,3 @@ -.. autoclass:: testcontainers.mailpit.MailpitUser -.. autoclass:: testcontainers.mailpit.MailpitContainer -.. title:: testcontainers.mailpit.MailpitContainer +.. autoclass:: testcontainers.community.mailpit.MailpitUser +.. autoclass:: testcontainers.community.mailpit.MailpitContainer +.. title:: testcontainers.community.mailpit.MailpitContainer diff --git a/docs/community/memcached.rst b/docs/community/memcached.rst index 83cd9c82e..93eaf0924 100644 --- a/docs/community/memcached.rst +++ b/docs/community/memcached.rst @@ -1 +1 @@ -.. autoclass:: testcontainers.memcached.MemcachedContainer +.. autoclass:: testcontainers.community.memcached.MemcachedContainer diff --git a/docs/community/milvus.rst b/docs/community/milvus.rst index f823d7fe9..123501adf 100644 --- a/docs/community/milvus.rst +++ b/docs/community/milvus.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.milvus.MilvusContainer -.. title:: testcontainers.milvus.MilvusContainer +.. autoclass:: testcontainers.community.milvus.MilvusContainer +.. title:: testcontainers.community.milvus.MilvusContainer diff --git a/docs/community/minio.rst b/docs/community/minio.rst index 409151787..6ead4df8c 100644 --- a/docs/community/minio.rst +++ b/docs/community/minio.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.minio.MinioContainer -.. title:: testcontainers.minio.MinioContainer +.. autoclass:: testcontainers.community.minio.MinioContainer +.. title:: testcontainers.community.minio.MinioContainer diff --git a/docs/community/mongodb.rst b/docs/community/mongodb.rst index 4c2af683e..40afcc0db 100644 --- a/docs/community/mongodb.rst +++ b/docs/community/mongodb.rst @@ -1,3 +1,3 @@ -.. autoclass:: testcontainers.mongodb.MongoDbContainer -.. autoclass:: testcontainers.mongodb.MongoDBAtlasLocalContainer -.. title:: testcontainers.mongodb.MongoDbContainer +.. autoclass:: testcontainers.community.mongodb.MongoDbContainer +.. autoclass:: testcontainers.community.mongodb.MongoDBAtlasLocalContainer +.. title:: testcontainers.community.mongodb.MongoDbContainer diff --git a/docs/community/mqtt.rst b/docs/community/mqtt.rst index 2e088cbbb..fe6e4c688 100644 --- a/docs/community/mqtt.rst +++ b/docs/community/mqtt.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.mqtt.MosquittoContainer -.. title:: testcontainers.mqtt.MosquittoContainer +.. autoclass:: testcontainers.community.mqtt.MosquittoContainer +.. title:: testcontainers.community.mqtt.MosquittoContainer diff --git a/docs/community/mssql.rst b/docs/community/mssql.rst index 56368ca71..df12b9315 100644 --- a/docs/community/mssql.rst +++ b/docs/community/mssql.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.mssql.SqlServerContainer -.. title:: testcontainers.mssql.SqlServerContainer +.. autoclass:: testcontainers.community.mssql.SqlServerContainer +.. title:: testcontainers.community.mssql.SqlServerContainer diff --git a/docs/community/mysql.rst b/docs/community/mysql.rst index d69785cd7..e404f39b7 100644 --- a/docs/community/mysql.rst +++ b/docs/community/mysql.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.mysql.MySqlContainer -.. title:: testcontainers.mysql.MySqlContainer +.. autoclass:: testcontainers.community.mysql.MySqlContainer +.. title:: testcontainers.community.mysql.MySqlContainer diff --git a/docs/community/nats.rst b/docs/community/nats.rst index 785892939..4cc5d7572 100644 --- a/docs/community/nats.rst +++ b/docs/community/nats.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.nats.NatsContainer -.. title:: testcontainers.nats.NatsContainer +.. autoclass:: testcontainers.community.nats.NatsContainer +.. title:: testcontainers.community.nats.NatsContainer diff --git a/docs/community/neo4j.rst b/docs/community/neo4j.rst index 885d4a598..6a4f0ca1a 100644 --- a/docs/community/neo4j.rst +++ b/docs/community/neo4j.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.neo4j.Neo4jContainer -.. title:: testcontainers.neo4j.Neo4jContainer +.. autoclass:: testcontainers.community.neo4j.Neo4jContainer +.. title:: testcontainers.community.neo4j.Neo4jContainer diff --git a/docs/community/nginx.rst b/docs/community/nginx.rst index a949a93d1..7b0b85b5a 100644 --- a/docs/community/nginx.rst +++ b/docs/community/nginx.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.nginx.NginxContainer -.. title:: testcontainers.nginx.NginxContainer +.. autoclass:: testcontainers.community.nginx.NginxContainer +.. title:: testcontainers.community.nginx.NginxContainer diff --git a/docs/community/ollama.rst b/docs/community/ollama.rst index dc18fe265..addc63e7d 100644 --- a/docs/community/ollama.rst +++ b/docs/community/ollama.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.ollama.OllamaContainer -.. title:: testcontainers.ollama.OllamaContainer +.. autoclass:: testcontainers.community.ollama.OllamaContainer +.. title:: testcontainers.community.ollama.OllamaContainer diff --git a/docs/community/openfga.rst b/docs/community/openfga.rst index 765b39f3a..dae13a6ee 100644 --- a/docs/community/openfga.rst +++ b/docs/community/openfga.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.openfga.OpenFGAContainer -.. title:: testcontainers.openfga.OpenFGAContainer +.. autoclass:: testcontainers.community.openfga.OpenFGAContainer +.. title:: testcontainers.community.openfga.OpenFGAContainer diff --git a/docs/community/opensearch.rst b/docs/community/opensearch.rst index fdc450fd6..f6c29d037 100644 --- a/docs/community/opensearch.rst +++ b/docs/community/opensearch.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.opensearch.OpenSearchContainer -.. title:: testcontainers.opensearch.OpenSearchContainer +.. autoclass:: testcontainers.community.opensearch.OpenSearchContainer +.. title:: testcontainers.community.opensearch.OpenSearchContainer diff --git a/docs/community/oracle-free.rst b/docs/community/oracle-free.rst index bbd5d3b5d..68f51ef79 100644 --- a/docs/community/oracle-free.rst +++ b/docs/community/oracle-free.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.oracle.OracleDbContainer -.. title:: testcontainers.oracle.OracleDbContainer +.. autoclass:: testcontainers.community.oracle.OracleDbContainer +.. title:: testcontainers.community.oracle.OracleDbContainer diff --git a/docs/community/postgres.rst b/docs/community/postgres.rst index bce939244..12a5450c9 100644 --- a/docs/community/postgres.rst +++ b/docs/community/postgres.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.postgres.PostgresContainer -.. title:: testcontainers.postgres.PostgresContainer +.. autoclass:: testcontainers.community.postgres.PostgresContainer +.. title:: testcontainers.community.postgres.PostgresContainer diff --git a/docs/community/qdrant.rst b/docs/community/qdrant.rst index 643096a37..3b4a8a2b3 100644 --- a/docs/community/qdrant.rst +++ b/docs/community/qdrant.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.qdrant.QdrantContainer -.. title:: testcontainers.qdrant.QdrantContainer +.. autoclass:: testcontainers.community.qdrant.QdrantContainer +.. title:: testcontainers.community.qdrant.QdrantContainer diff --git a/docs/community/rabbitmq.rst b/docs/community/rabbitmq.rst index 1b31362d3..44e5ce601 100644 --- a/docs/community/rabbitmq.rst +++ b/docs/community/rabbitmq.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.rabbitmq.RabbitMqContainer -.. title:: testcontainers.rabbitmq.RabbitMqContainer +.. autoclass:: testcontainers.community.rabbitmq.RabbitMqContainer +.. title:: testcontainers.community.rabbitmq.RabbitMqContainer diff --git a/docs/community/redis.rst b/docs/community/redis.rst index 1dfcf9e35..b69a7a27f 100644 --- a/docs/community/redis.rst +++ b/docs/community/redis.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.redis.RedisContainer -.. title:: testcontainers.redis.RedisContainer +.. autoclass:: testcontainers.community.redis.RedisContainer +.. title:: testcontainers.community.redis.RedisContainer diff --git a/docs/community/registry.rst b/docs/community/registry.rst index f503f338e..482105ffc 100644 --- a/docs/community/registry.rst +++ b/docs/community/registry.rst @@ -1,4 +1,4 @@ -.. autoclass:: testcontainers.registry.DockerRegistryContainer +.. autoclass:: testcontainers.community.registry.DockerRegistryContainer When building Docker containers with Docker Buildx there is currently no option to test your containers locally without a local registry. Otherwise Buildx pushes your image to Docker Hub, which is not what you want in a test case. More diff --git a/docs/community/scylla.rst b/docs/community/scylla.rst index fd1ea03fb..bdc4608d0 100644 --- a/docs/community/scylla.rst +++ b/docs/community/scylla.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.scylla.ScyllaContainer -.. title:: testcontainers.scylla.ScyllaContainer +.. autoclass:: testcontainers.community.scylla.ScyllaContainer +.. title:: testcontainers.community.scylla.ScyllaContainer diff --git a/docs/community/selenium.rst b/docs/community/selenium.rst index 5beef79cd..d67246d96 100644 --- a/docs/community/selenium.rst +++ b/docs/community/selenium.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.selenium.BrowserWebDriverContainer -.. title:: testcontainers.selenium.BrowserWebDriverContainer +.. autoclass:: testcontainers.community.selenium.BrowserWebDriverContainer +.. title:: testcontainers.community.selenium.BrowserWebDriverContainer diff --git a/docs/community/sftp.rst b/docs/community/sftp.rst index 2287d59c9..9503fb60c 100644 --- a/docs/community/sftp.rst +++ b/docs/community/sftp.rst @@ -1,3 +1,3 @@ -.. autoclass:: testcontainers.sftp.SFTPContainer -.. autoclass:: testcontainers.sftp.SFTPUser -.. title:: testcontainers.sftp.SFTPContainer +.. autoclass:: testcontainers.community.sftp.SFTPContainer +.. autoclass:: testcontainers.community.sftp.SFTPUser +.. title:: testcontainers.community.sftp.SFTPContainer diff --git a/docs/community/trino.rst b/docs/community/trino.rst index 95b4be930..fa6ba1003 100644 --- a/docs/community/trino.rst +++ b/docs/community/trino.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.trino.TrinoContainer -.. title:: testcontainers.trino.TrinoContainer +.. autoclass:: testcontainers.community.trino.TrinoContainer +.. title:: testcontainers.community.trino.TrinoContainer diff --git a/docs/community/valkey.rst b/docs/community/valkey.rst index abe0c74e1..24ab8a96b 100644 --- a/docs/community/valkey.rst +++ b/docs/community/valkey.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.valkey.ValkeyContainer -.. title:: testcontainers.valkey.ValkeyContainer +.. autoclass:: testcontainers.community.valkey.ValkeyContainer +.. title:: testcontainers.community.valkey.ValkeyContainer diff --git a/docs/community/vault.rst b/docs/community/vault.rst index 71c079dfa..dde0e90b1 100644 --- a/docs/community/vault.rst +++ b/docs/community/vault.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.vault.VaultContainer -.. title:: testcontainers.vault.VaultContainer +.. autoclass:: testcontainers.community.vault.VaultContainer +.. title:: testcontainers.community.vault.VaultContainer diff --git a/docs/community/weaviate.rst b/docs/community/weaviate.rst index 560934164..33e22168f 100644 --- a/docs/community/weaviate.rst +++ b/docs/community/weaviate.rst @@ -1,2 +1,2 @@ -.. autoclass:: testcontainers.weaviate.WeaviateContainer -.. title:: testcontainers.weaviate.WeaviateContainer +.. autoclass:: testcontainers.community.weaviate.WeaviateContainer +.. title:: testcontainers.community.weaviate.WeaviateContainer diff --git a/docs/features/authentication.md b/docs/features/authentication.md index 138fd6816..239271768 100644 --- a/docs/features/authentication.md +++ b/docs/features/authentication.md @@ -7,7 +7,7 @@ Testcontainers-Python supports various methods of authenticating with Docker reg The simplest way to authenticate is using Docker's built-in credential store. Testcontainers-Python will automatically use credentials stored by Docker: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer # Docker will automatically use stored credentials container = GenericContainer("private.registry.com/myimage:latest") @@ -56,7 +56,7 @@ container = GenericContainer("private.registry.com/myimage:latest") For Amazon Elastic Container Registry (ECR), Testcontainers-Python supports automatic authentication: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer # ECR authentication is handled automatically container = GenericContainer("123456789012.dkr.ecr.region.amazonaws.com/myimage:latest") @@ -67,7 +67,7 @@ container = GenericContainer("123456789012.dkr.ecr.region.amazonaws.com/myimage: For Google Container Registry, you can use Google Cloud credentials: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer # GCR authentication using Google Cloud credentials container = GenericContainer("gcr.io/myproject/myimage:latest") @@ -78,7 +78,7 @@ container = GenericContainer("gcr.io/myproject/myimage:latest") For Azure Container Registry, you can use Azure credentials: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer # ACR authentication using Azure credentials container = GenericContainer("myregistry.azurecr.io/myimage:latest") diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md index c4fe06069..01cbc5f82 100644 --- a/docs/features/container_logs.md +++ b/docs/features/container_logs.md @@ -7,7 +7,7 @@ Testcontainers-Python provides several ways to access and follow container logs. The simplest way to access logs is using the `get_logs` method: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer with GenericContainer("nginx:alpine") as container: # Get all logs @@ -80,7 +80,7 @@ Here's how to use logs in tests: ```python import pytest -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer def test_container_logs(): with GenericContainer("nginx:alpine") as container: diff --git a/docs/features/copying_data.md b/docs/features/copying_data.md index 8623ec73f..2d5a8f11b 100644 --- a/docs/features/copying_data.md +++ b/docs/features/copying_data.md @@ -7,7 +7,7 @@ Testcontainers-Python provides several ways to copy data into containers. This i The simplest way to copy a file is using the `copy_file_to_container` method: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer with GenericContainer("alpine:latest") as container: # Copy a single file diff --git a/docs/features/creating_container.md b/docs/features/creating_container.md index 40c632943..9f83772e8 100644 --- a/docs/features/creating_container.md +++ b/docs/features/creating_container.md @@ -12,7 +12,7 @@ Testcontainers-Python is a thin wrapper around Docker designed for use in tests. The simplest way to create a container is using the `GenericContainer` class: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer def test_basic_container(): with GenericContainer("nginx:alpine") as nginx: @@ -109,7 +109,7 @@ finally: 3. **Pytest fixtures:** ```python import pytest -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer @pytest.fixture def nginx_container(): @@ -133,7 +133,7 @@ For details on waiting for containers to be ready, see [Wait strategies](wait_st You can get detailed information about containers using the `get_container_info()` method. This works with both `DockerContainer` and `ComposeContainer`: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer def test_container_info(): with GenericContainer("nginx:alpine") as container: diff --git a/docs/features/executing_commands.md b/docs/features/executing_commands.md index 9db76a89c..de44ec3aa 100644 --- a/docs/features/executing_commands.md +++ b/docs/features/executing_commands.md @@ -7,7 +7,7 @@ Testcontainers-Python provides several ways to execute commands inside container The simplest way to execute a command is using the `exec` method: ```python -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer with GenericContainer("alpine:latest") as container: # Execute a simple command @@ -108,7 +108,7 @@ with GenericContainer("alpine:latest") as container: ### Database Setup ```python -from testcontainers.postgres import PostgresContainer +from testcontainers.community.postgres import PostgresContainer with PostgresContainer() as postgres: # Create a database diff --git a/docs/features/networking.md b/docs/features/networking.md index 6ebabe532..c0a8275ab 100644 --- a/docs/features/networking.md +++ b/docs/features/networking.md @@ -135,8 +135,8 @@ Here's a complete example of a multi-container application: ```python from testcontainers.core.network import Network -from testcontainers.postgres import PostgresContainer -from testcontainers.redis import RedisContainer +from testcontainers.community.postgres import PostgresContainer +from testcontainers.community.redis import RedisContainer def test_multi_container_app(): # Create a network diff --git a/docs/features/wait_strategies.md b/docs/features/wait_strategies.md index a8b1351ae..65fad8ff1 100644 --- a/docs/features/wait_strategies.md +++ b/docs/features/wait_strategies.md @@ -83,8 +83,8 @@ container = GenericContainer( Many container implementations include built-in connection waiting. For example: ```python -from testcontainers.redis import RedisContainer -from testcontainers.postgres import PostgresContainer +from testcontainers.community.redis import RedisContainer +from testcontainers.community.postgres import PostgresContainer # Redis container waits for connection redis = RedisContainer() diff --git a/docs/index.rst b/docs/index.rst index eb7cf40f3..b906e3ed9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,7 +24,7 @@ Getting Started .. doctest:: - >>> from testcontainers.postgres import PostgresContainer + >>> from testcontainers.community.postgres import PostgresContainer >>> import sqlalchemy >>> with PostgresContainer("postgres:16") as postgres: @@ -39,7 +39,7 @@ The snippet above will spin up the current latest version of a postgres database .. doctest:: - >>> from testcontainers.postgres import PostgresContainer + >>> from testcontainers.community.postgres import PostgresContainer >>> import psycopg >>> with PostgresContainer("postgres:16", driver=None) as postgres: diff --git a/docs/modules/arangodb_example.py b/docs/modules/arangodb_example.py index e75467610..94f17725a 100644 --- a/docs/modules/arangodb_example.py +++ b/docs/modules/arangodb_example.py @@ -2,7 +2,7 @@ from arango import ArangoClient -from testcontainers.arangodb import ArangoDbContainer +from testcontainers.community.arangodb import ArangoDbContainer def basic_example(): diff --git a/docs/modules/aws_example.py b/docs/modules/aws_example.py index 64410ed23..0fca74f53 100644 --- a/docs/modules/aws_example.py +++ b/docs/modules/aws_example.py @@ -3,11 +3,11 @@ import boto3 -from testcontainers.aws import AwsContainer +from testcontainers.community.aws import AWSLambdaContainer def basic_example(): - with AwsContainer() as aws: + with AWSLambdaContainer() as aws: # Get connection parameters host = aws.get_container_host_ip() port = aws.get_exposed_port(aws.port) diff --git a/docs/modules/azurite_example.py b/docs/modules/azurite_example.py index 872046e97..ef59e8e98 100644 --- a/docs/modules/azurite_example.py +++ b/docs/modules/azurite_example.py @@ -3,7 +3,7 @@ from azure.storage.blob import BlobServiceClient from azure.storage.queue import QueueServiceClient -from testcontainers.azurite import AzuriteContainer +from testcontainers.community.azurite import AzuriteContainer def basic_example(): diff --git a/docs/modules/cassandra_example.py b/docs/modules/cassandra_example.py index 54cee6f64..53bfa2db8 100644 --- a/docs/modules/cassandra_example.py +++ b/docs/modules/cassandra_example.py @@ -4,7 +4,7 @@ from cassandra.auth import PlainTextAuthProvider from cassandra.cluster import Cluster -from testcontainers.cassandra import CassandraContainer +from testcontainers.community.cassandra import CassandraContainer def basic_example(): diff --git a/docs/modules/chroma_example.py b/docs/modules/chroma_example.py index 3d22c01c7..52f4a5453 100644 --- a/docs/modules/chroma_example.py +++ b/docs/modules/chroma_example.py @@ -1,7 +1,7 @@ import chromadb from chromadb.config import Settings -from testcontainers.chroma import ChromaContainer +from testcontainers.community.chroma import ChromaContainer def basic_example(): diff --git a/docs/modules/clickhouse_example.py b/docs/modules/clickhouse_example.py index 1b4eb5c8d..dad564d20 100644 --- a/docs/modules/clickhouse_example.py +++ b/docs/modules/clickhouse_example.py @@ -3,7 +3,7 @@ import pandas as pd from clickhouse_driver import Client -from testcontainers.clickhouse import ClickHouseContainer +from testcontainers.community.clickhouse import ClickHouseContainer def basic_example(): diff --git a/docs/modules/cockroachdb_example.py b/docs/modules/cockroachdb_example.py index 9da3f219c..77aaf4eb8 100644 --- a/docs/modules/cockroachdb_example.py +++ b/docs/modules/cockroachdb_example.py @@ -2,7 +2,7 @@ import sqlalchemy from sqlalchemy import text -from testcontainers.cockroachdb import CockroachContainer +from testcontainers.community.cockroachdb import CockroachContainer def basic_example(): diff --git a/docs/modules/cosmosdb_example.py b/docs/modules/cosmosdb_example.py index c836a1409..7c9a3fed2 100644 --- a/docs/modules/cosmosdb_example.py +++ b/docs/modules/cosmosdb_example.py @@ -2,7 +2,7 @@ from azure.cosmos import CosmosClient, PartitionKey -from testcontainers.cosmosdb import CosmosDbContainer +from testcontainers.community.cosmosdb import CosmosDbContainer def basic_example(): diff --git a/docs/modules/db2_example.py b/docs/modules/db2_example.py index 97b5d65f5..6aa1d6153 100644 --- a/docs/modules/db2_example.py +++ b/docs/modules/db2_example.py @@ -2,7 +2,7 @@ import ibm_db_dbi import pandas as pd -from testcontainers.db2 import Db2Container +from testcontainers.community.db2 import Db2Container def basic_example(): diff --git a/docs/modules/elasticsearch_example.py b/docs/modules/elasticsearch_example.py index 1b3ed4077..036376f95 100644 --- a/docs/modules/elasticsearch_example.py +++ b/docs/modules/elasticsearch_example.py @@ -3,7 +3,7 @@ from elasticsearch import Elasticsearch -from testcontainers.elasticsearch import ElasticsearchContainer +from testcontainers.community.elasticsearch import ElasticsearchContainer def basic_example(): diff --git a/docs/modules/generic_example.py b/docs/modules/generic_example.py index 107bcc7c2..caa3eafab 100644 --- a/docs/modules/generic_example.py +++ b/docs/modules/generic_example.py @@ -1,6 +1,6 @@ import requests -from testcontainers.generic import GenericContainer +from testcontainers.community.generic import GenericContainer def basic_example(): diff --git a/docs/modules/google_example.py b/docs/modules/google_example.py index 323b25817..04d985644 100644 --- a/docs/modules/google_example.py +++ b/docs/modules/google_example.py @@ -3,7 +3,7 @@ from google.cloud import bigquery, datastore, pubsub, storage -from testcontainers.google import GoogleContainer +from testcontainers.community.google import GoogleContainer def basic_example(): diff --git a/docs/modules/influxdb_example.py b/docs/modules/influxdb_example.py index 94154b034..97391a25f 100644 --- a/docs/modules/influxdb_example.py +++ b/docs/modules/influxdb_example.py @@ -4,7 +4,7 @@ from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS -from testcontainers.influxdb import InfluxDBContainer +from testcontainers.community.influxdb import InfluxDBContainer def basic_example(): diff --git a/docs/modules/k3s_example.py b/docs/modules/k3s_example.py index 75550f0b6..bf566d2af 100644 --- a/docs/modules/k3s_example.py +++ b/docs/modules/k3s_example.py @@ -5,7 +5,7 @@ from kubernetes import client, config from kubernetes.client.rest import ApiException -from testcontainers.k3s import K3sContainer +from testcontainers.community.k3s import K3sContainer def basic_example(): diff --git a/docs/modules/kafka_example.py b/docs/modules/kafka_example.py index 37b9a32d0..1c0df4128 100644 --- a/docs/modules/kafka_example.py +++ b/docs/modules/kafka_example.py @@ -5,7 +5,7 @@ from kafka import KafkaConsumer, KafkaProducer -from testcontainers.kafka import KafkaContainer +from testcontainers.community.kafka import KafkaContainer def basic_example(): diff --git a/docs/modules/keycloak_example.py b/docs/modules/keycloak_example.py index f4299f989..712dac2e5 100644 --- a/docs/modules/keycloak_example.py +++ b/docs/modules/keycloak_example.py @@ -2,7 +2,7 @@ from keycloak import KeycloakAdmin, KeycloakOpenID -from testcontainers.keycloak import KeycloakContainer +from testcontainers.community.keycloak import KeycloakContainer def basic_example(): diff --git a/docs/modules/localstack_example.py b/docs/modules/localstack_example.py index 8c622f223..19767caad 100644 --- a/docs/modules/localstack_example.py +++ b/docs/modules/localstack_example.py @@ -2,7 +2,7 @@ import boto3 -from testcontainers.localstack import LocalStackContainer +from testcontainers.community.localstack import LocalStackContainer def basic_example(): diff --git a/docs/modules/mailpit_example.py b/docs/modules/mailpit_example.py index ef97ab906..c44934c26 100644 --- a/docs/modules/mailpit_example.py +++ b/docs/modules/mailpit_example.py @@ -5,7 +5,7 @@ import requests -from testcontainers.mailpit import MailpitContainer +from testcontainers.community.mailpit import MailpitContainer def basic_example(): diff --git a/docs/modules/memcached_example.py b/docs/modules/memcached_example.py index 01e52dea8..e84daccc6 100644 --- a/docs/modules/memcached_example.py +++ b/docs/modules/memcached_example.py @@ -3,7 +3,7 @@ import memcache -from testcontainers.memcached import MemcachedContainer +from testcontainers.community.memcached import MemcachedContainer def basic_example(): diff --git a/docs/modules/milvus_example.py b/docs/modules/milvus_example.py index 776aa11b3..3d877bd63 100644 --- a/docs/modules/milvus_example.py +++ b/docs/modules/milvus_example.py @@ -4,7 +4,7 @@ import numpy as np from pymilvus import Collection, CollectionSchema, DataType, FieldSchema, connections, utility -from testcontainers.milvus import MilvusContainer +from testcontainers.community.milvus import MilvusContainer def basic_example(): diff --git a/docs/modules/minio_example.py b/docs/modules/minio_example.py index 5318679be..106b037ba 100644 --- a/docs/modules/minio_example.py +++ b/docs/modules/minio_example.py @@ -4,7 +4,7 @@ from minio import Minio -from testcontainers.minio import MinioContainer +from testcontainers.community.minio import MinioContainer def basic_example(): diff --git a/docs/modules/mongodb_example.py b/docs/modules/mongodb_example.py index 8fde30c65..0ecfe89c0 100644 --- a/docs/modules/mongodb_example.py +++ b/docs/modules/mongodb_example.py @@ -3,7 +3,7 @@ from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer +from testcontainers.community.mongodb import MongoDbContainer def basic_example(): diff --git a/docs/modules/mqtt_example.py b/docs/modules/mqtt_example.py index dc6de9fe3..8ab3e564a 100644 --- a/docs/modules/mqtt_example.py +++ b/docs/modules/mqtt_example.py @@ -2,7 +2,7 @@ import paho.mqtt.client as mqtt -from testcontainers.mqtt import MqttContainer +from testcontainers.community.mqtt import MqttContainer def basic_example(): diff --git a/docs/modules/mssql_example.py b/docs/modules/mssql_example.py index f42e541d1..e518c5a86 100644 --- a/docs/modules/mssql_example.py +++ b/docs/modules/mssql_example.py @@ -1,6 +1,6 @@ import pymssql -from testcontainers.mssql import MsSqlContainer +from testcontainers.community.mssql import MsSqlContainer def basic_example(): diff --git a/docs/modules/mysql_example.py b/docs/modules/mysql_example.py index ba3418b28..2ce977701 100644 --- a/docs/modules/mysql_example.py +++ b/docs/modules/mysql_example.py @@ -1,6 +1,6 @@ import sqlalchemy -from testcontainers.mysql import MySqlContainer +from testcontainers.community.mysql import MySqlContainer def basic_example(): diff --git a/docs/modules/nats_example.py b/docs/modules/nats_example.py index 094b94856..660f74784 100644 --- a/docs/modules/nats_example.py +++ b/docs/modules/nats_example.py @@ -4,7 +4,7 @@ from nats.aio.client import Client as NATS from nats.aio.msg import Msg -from testcontainers.nats import NatsContainer +from testcontainers.community.nats import NatsContainer async def message_handler(msg: Msg): diff --git a/docs/modules/neo4j_example.py b/docs/modules/neo4j_example.py index c6114bc70..7bd446764 100644 --- a/docs/modules/neo4j_example.py +++ b/docs/modules/neo4j_example.py @@ -2,7 +2,7 @@ from neo4j import GraphDatabase -from testcontainers.neo4j import Neo4jContainer +from testcontainers.community.neo4j import Neo4jContainer def basic_example(): diff --git a/docs/modules/nginx_example.py b/docs/modules/nginx_example.py index d7aaec122..e5ac30980 100644 --- a/docs/modules/nginx_example.py +++ b/docs/modules/nginx_example.py @@ -4,7 +4,7 @@ import requests -from testcontainers.nginx import NginxContainer +from testcontainers.community.nginx import NginxContainer def basic_example(): diff --git a/docs/modules/ollama_example.py b/docs/modules/ollama_example.py index 134b636f5..a5ab6c2e0 100644 --- a/docs/modules/ollama_example.py +++ b/docs/modules/ollama_example.py @@ -1,6 +1,6 @@ import requests -from testcontainers.ollama import OllamaContainer +from testcontainers.community.ollama import OllamaContainer def basic_example(): diff --git a/docs/modules/postgres_example.py b/docs/modules/postgres_example.py index 611081023..bc6a80366 100644 --- a/docs/modules/postgres_example.py +++ b/docs/modules/postgres_example.py @@ -2,7 +2,7 @@ import sqlalchemy from sqlalchemy import text -from testcontainers.postgres import PostgresContainer +from testcontainers.community.postgres import PostgresContainer def basic_example(): diff --git a/docs/modules/qdrant_example.py b/docs/modules/qdrant_example.py index 589735e1e..d8ce029d8 100644 --- a/docs/modules/qdrant_example.py +++ b/docs/modules/qdrant_example.py @@ -5,7 +5,7 @@ from qdrant_client import QdrantClient from qdrant_client.http import models -from testcontainers.qdrant import QdrantContainer +from testcontainers.community.qdrant import QdrantContainer def basic_example(): diff --git a/docs/modules/rabbitmq_example.py b/docs/modules/rabbitmq_example.py index 906a0e24f..fc79bbde7 100644 --- a/docs/modules/rabbitmq_example.py +++ b/docs/modules/rabbitmq_example.py @@ -4,7 +4,7 @@ import pika -from testcontainers.rabbitmq import RabbitMQContainer +from testcontainers.community.rabbitmq import RabbitMQContainer def basic_example(): diff --git a/docs/modules/redis_example.py b/docs/modules/redis_example.py index 5fce0a7b7..374b45619 100644 --- a/docs/modules/redis_example.py +++ b/docs/modules/redis_example.py @@ -2,7 +2,7 @@ import redis -from testcontainers.redis import RedisContainer +from testcontainers.community.redis import RedisContainer def basic_example(): diff --git a/docs/modules/registry_example.py b/docs/modules/registry_example.py index 0bd136872..6b8289d17 100644 --- a/docs/modules/registry_example.py +++ b/docs/modules/registry_example.py @@ -2,7 +2,7 @@ import requests -from testcontainers.registry import RegistryContainer +from testcontainers.community.registry import RegistryContainer def basic_example(): diff --git a/docs/modules/scylla_example.py b/docs/modules/scylla_example.py index fa26369cc..7cc913955 100644 --- a/docs/modules/scylla_example.py +++ b/docs/modules/scylla_example.py @@ -4,7 +4,7 @@ from cassandra.auth import PlainTextAuthProvider from cassandra.cluster import Cluster -from testcontainers.scylla import ScyllaContainer +from testcontainers.community.scylla import ScyllaContainer def basic_example(): diff --git a/docs/modules/selenium_example.py b/docs/modules/selenium_example.py index f136126fb..355e3d6a6 100644 --- a/docs/modules/selenium_example.py +++ b/docs/modules/selenium_example.py @@ -2,7 +2,7 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait -from testcontainers.selenium import SeleniumContainer +from testcontainers.community.selenium import SeleniumContainer def basic_example(): diff --git a/docs/modules/sftp_example.py b/docs/modules/sftp_example.py index f5d2058eb..279729e13 100644 --- a/docs/modules/sftp_example.py +++ b/docs/modules/sftp_example.py @@ -4,7 +4,7 @@ import paramiko -from testcontainers.sftp import SftpContainer +from testcontainers.community.sftp import SftpContainer def basic_example(): diff --git a/docs/modules/trino_example.py b/docs/modules/trino_example.py index f2b351243..b2466ca23 100644 --- a/docs/modules/trino_example.py +++ b/docs/modules/trino_example.py @@ -1,7 +1,7 @@ import trino from trino.exceptions import TrinoQueryError -from testcontainers.trino import TrinoContainer +from testcontainers.community.trino import TrinoContainer def basic_example(): diff --git a/docs/modules/valkey_example.py b/docs/modules/valkey_example.py index 1288b5d94..1704a4e5a 100644 --- a/docs/modules/valkey_example.py +++ b/docs/modules/valkey_example.py @@ -6,7 +6,7 @@ from glide_sync import GlideClient, GlideClientConfiguration, NodeAddress, ServerCredentials -from testcontainers.valkey import ValkeyContainer +from testcontainers.community.valkey import ValkeyContainer def basic_example(): diff --git a/docs/modules/vault_example.py b/docs/modules/vault_example.py index 2dd873f7a..872f5f577 100644 --- a/docs/modules/vault_example.py +++ b/docs/modules/vault_example.py @@ -2,7 +2,7 @@ import hvac -from testcontainers.vault import VaultContainer +from testcontainers.community.vault import VaultContainer def basic_example(): diff --git a/docs/modules/weaviate_example.py b/docs/modules/weaviate_example.py index 0c7097723..0feb6da0b 100644 --- a/docs/modules/weaviate_example.py +++ b/docs/modules/weaviate_example.py @@ -3,7 +3,7 @@ import weaviate -from testcontainers.weaviate import WeaviateContainer +from testcontainers.community.weaviate import WeaviateContainer def basic_example(): diff --git a/docs/quickstart.md b/docs/quickstart.md index 83b0454ff..aaad2fc32 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -18,7 +18,7 @@ pip install testcontainers ```python import pytest -from testcontainers.redis import RedisContainer +from testcontainers.community.redis import RedisContainer import redis def test_with_redis(): diff --git a/src/testcontainers/community/arangodb/__init__.py b/src/testcontainers/community/arangodb/__init__.py index 9ea36f6ea..c81898ca2 100644 --- a/src/testcontainers/community/arangodb/__init__.py +++ b/src/testcontainers/community/arangodb/__init__.py @@ -23,7 +23,7 @@ class ArangoDbContainer(DbContainer): .. doctest:: - >>> from testcontainers.arangodb import ArangoDbContainer + >>> from testcontainers.community.arangodb import ArangoDbContainer >>> from arango import ArangoClient >>> with ArangoDbContainer("arangodb:3.11.8") as arango: diff --git a/src/testcontainers/community/aws/aws_lambda.py b/src/testcontainers/community/aws/aws_lambda.py index e3cd76faf..31565206b 100644 --- a/src/testcontainers/community/aws/aws_lambda.py +++ b/src/testcontainers/community/aws/aws_lambda.py @@ -2,9 +2,8 @@ from typing import Union import httpx - +from testcontainers.community.generic.server import ServerContainer from testcontainers.core.image import DockerImage -from testcontainers.generic.server import ServerContainer RIE_PATH = "/2015-03-31/functions/function/invocations" # AWS OS-only base images contain an Amazon Linux distribution and the runtime interface emulator (RIE) for Lambda. @@ -20,7 +19,7 @@ class AWSLambdaContainer(ServerContainer): .. doctest:: - >>> from testcontainers.aws import AWSLambdaContainer + >>> from testcontainers.community.aws import AWSLambdaContainer >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage diff --git a/src/testcontainers/community/azurite/__init__.py b/src/testcontainers/community/azurite/__init__.py index 3cd755f34..0cc400200 100644 --- a/src/testcontainers/community/azurite/__init__.py +++ b/src/testcontainers/community/azurite/__init__.py @@ -44,7 +44,7 @@ class AzuriteContainer(DockerContainer): .. doctest:: - >>> from testcontainers.azurite import AzuriteContainer + >>> from testcontainers.community.azurite import AzuriteContainer >>> from azure.storage.blob import BlobServiceClient >>> with AzuriteContainer() as azurite_container: diff --git a/src/testcontainers/community/cassandra/__init__.py b/src/testcontainers/community/cassandra/__init__.py index 2770451fa..813be8b1d 100644 --- a/src/testcontainers/community/cassandra/__init__.py +++ b/src/testcontainers/community/cassandra/__init__.py @@ -23,7 +23,7 @@ class CassandraContainer(DockerContainer): .. doctest:: cassandra_container :skipif: SKIP_CASSANDRA_EXAMPLE - >>> from testcontainers.cassandra import CassandraContainer + >>> from testcontainers.community.cassandra import CassandraContainer >>> from cassandra.cluster import Cluster, DCAwareRoundRobinPolicy >>> with CassandraContainer("cassandra:4.1.4") as cassandra, Cluster( diff --git a/src/testcontainers/community/chroma/__init__.py b/src/testcontainers/community/chroma/__init__.py index 358351b82..aa6c0b3fc 100644 --- a/src/testcontainers/community/chroma/__init__.py +++ b/src/testcontainers/community/chroma/__init__.py @@ -1,7 +1,6 @@ from typing import TYPE_CHECKING from requests import ConnectionError, get - from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import wait_container_is_ready @@ -20,7 +19,7 @@ class ChromaContainer(DockerContainer): .. doctest:: >>> import chromadb - >>> from testcontainers.chroma import ChromaContainer + >>> from testcontainers.community.chroma import ChromaContainer >>> with ChromaContainer() as chroma: ... config = chroma.get_config() diff --git a/src/testcontainers/community/clickhouse/__init__.py b/src/testcontainers/community/clickhouse/__init__.py index 00ebde809..c144e2a8a 100644 --- a/src/testcontainers/community/clickhouse/__init__.py +++ b/src/testcontainers/community/clickhouse/__init__.py @@ -30,7 +30,7 @@ class ClickHouseContainer(DbContainer): .. doctest:: >>> import clickhouse_driver - >>> from testcontainers.clickhouse import ClickHouseContainer + >>> from testcontainers.community.clickhouse import ClickHouseContainer >>> with ClickHouseContainer("clickhouse/clickhouse-server:21.8") as clickhouse: ... client = clickhouse_driver.Client.from_url(clickhouse.get_connection_url()) diff --git a/src/testcontainers/community/cockroachdb/__init__.py b/src/testcontainers/community/cockroachdb/__init__.py index 13a17ed5c..51bfdabd0 100644 --- a/src/testcontainers/community/cockroachdb/__init__.py +++ b/src/testcontainers/community/cockroachdb/__init__.py @@ -33,7 +33,7 @@ class CockroachDBContainer(DbContainer): .. doctest:: >>> import sqlalchemy - >>> from testcontainers.cockroachdb import CockroachDBContainer + >>> from testcontainers.community.cockroachdb import CockroachDBContainer >>> with CockroachDBContainer('cockroachdb/cockroach:v24.1.1') as crdb: ... engine = sqlalchemy.create_engine(crdb.get_connection_url()) diff --git a/src/testcontainers/community/cosmosdb/mongodb.py b/src/testcontainers/community/cosmosdb/mongodb.py index e54f52ff6..e31b917b9 100644 --- a/src/testcontainers/community/cosmosdb/mongodb.py +++ b/src/testcontainers/community/cosmosdb/mongodb.py @@ -15,7 +15,7 @@ class CosmosDBMongoEndpointContainer(CosmosDBEmulatorContainer): .. code-block:: python - >>> from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer + >>> from testcontainers.community.cosmosdb import CosmosDBMongoEndpointContainer >>> with CosmosDBMongoEndpointContainer(mongodb_version="4.0") as emulator: ... print(f"Point your MongoDB client at {emulator.host}:{emulator.port} using key {emulator.key}") diff --git a/src/testcontainers/community/cosmosdb/nosql.py b/src/testcontainers/community/cosmosdb/nosql.py index f78469674..58db8525b 100644 --- a/src/testcontainers/community/cosmosdb/nosql.py +++ b/src/testcontainers/community/cosmosdb/nosql.py @@ -1,7 +1,6 @@ from azure.core.exceptions import ServiceRequestError from azure.cosmos import CosmosClient as SyncCosmosClient from azure.cosmos.aio import CosmosClient as AsyncCosmosClient - from testcontainers.core.waiting_utils import wait_container_is_ready from ._emulator import CosmosDBEmulatorContainer @@ -19,13 +18,13 @@ class CosmosDBNoSQLEndpointContainer(CosmosDBEmulatorContainer): .. code-block:: python - >>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer + >>> from testcontainers.community.cosmosdb import CosmosDBNoSQLEndpointContainer >>> with CosmosDBNoSQLEndpointContainer() as emulator: ... db = emulator.insecure_sync_client().create_database_if_not_exists("test") .. code-block:: python - >>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer + >>> from testcontainers.community.cosmosdb import CosmosDBNoSQLEndpointContainer >>> from azure.cosmos import CosmosClient >>> with CosmosDBNoSQLEndpointContainer() as emulator: diff --git a/src/testcontainers/community/db2/__init__.py b/src/testcontainers/community/db2/__init__.py index b17c0efef..767e49f7f 100644 --- a/src/testcontainers/community/db2/__init__.py +++ b/src/testcontainers/community/db2/__init__.py @@ -14,7 +14,7 @@ class Db2Container(DbContainer): .. doctest:: >>> import sqlalchemy - >>> from testcontainers.db2 import Db2Container + >>> from testcontainers.community.db2 import Db2Container >>> with Db2Container("icr.io/db2_community/db2:latest") as db2: ... engine = sqlalchemy.create_engine(db2.get_connection_url()) diff --git a/src/testcontainers/community/elasticsearch/__init__.py b/src/testcontainers/community/elasticsearch/__init__.py index 914ebf0b5..ebda06b17 100644 --- a/src/testcontainers/community/elasticsearch/__init__.py +++ b/src/testcontainers/community/elasticsearch/__init__.py @@ -64,7 +64,7 @@ class ElasticSearchContainer(DockerContainer): >>> import json >>> import urllib - >>> from testcontainers.elasticsearch import ElasticSearchContainer + >>> from testcontainers.community.elasticsearch import ElasticSearchContainer >>> with ElasticSearchContainer(f'elasticsearch:8.3.3', mem_limit='3G') as es: ... resp = urllib.request.urlopen(f'http://{es.get_container_host_ip()}:{es.get_exposed_port(es.port)}') diff --git a/src/testcontainers/community/generic/server.py b/src/testcontainers/community/generic/server.py index 19a7c896c..1d54fd0fc 100644 --- a/src/testcontainers/community/generic/server.py +++ b/src/testcontainers/community/generic/server.py @@ -16,7 +16,7 @@ class ServerContainer(DockerContainer): .. doctest:: >>> import httpx - >>> from testcontainers.generic import ServerContainer + >>> from testcontainers.community.generic import ServerContainer >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage diff --git a/src/testcontainers/community/google/datastore.py b/src/testcontainers/community/google/datastore.py index 24edbdcd7..f97b38e8b 100644 --- a/src/testcontainers/community/google/datastore.py +++ b/src/testcontainers/community/google/datastore.py @@ -13,10 +13,11 @@ import os from unittest.mock import patch -from google.cloud import datastore from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs +from google.cloud import datastore + class DatastoreContainer(DockerContainer): """ @@ -30,7 +31,7 @@ class DatastoreContainer(DockerContainer): .. doctest:: - >>> from testcontainers.google import DatastoreContainer + >>> from testcontainers.community.google import DatastoreContainer >>> config = DatastoreContainer() >>> with config as datastore: diff --git a/src/testcontainers/community/google/pubsub.py b/src/testcontainers/community/google/pubsub.py index 706030599..b9ea4151c 100644 --- a/src/testcontainers/community/google/pubsub.py +++ b/src/testcontainers/community/google/pubsub.py @@ -13,9 +13,10 @@ import os from unittest.mock import patch -from google.cloud import pubsub from testcontainers.core.container import DockerContainer +from google.cloud import pubsub + class PubSubContainer(DockerContainer): """ @@ -30,7 +31,7 @@ class PubSubContainer(DockerContainer): .. doctest:: - >>> from testcontainers.google import PubSubContainer + >>> from testcontainers.community.google import PubSubContainer >>> config = PubSubContainer() >>> with config as pubsub: diff --git a/src/testcontainers/community/influxdb1/__init__.py b/src/testcontainers/community/influxdb1/__init__.py index 81f21c163..a516b6adc 100644 --- a/src/testcontainers/community/influxdb1/__init__.py +++ b/src/testcontainers/community/influxdb1/__init__.py @@ -14,8 +14,7 @@ from typing import Optional from influxdb import InfluxDBClient - -from testcontainers.influxdb import InfluxDbContainer +from testcontainers.community.influxdb import InfluxDbContainer class InfluxDb1Container(InfluxDbContainer): @@ -27,7 +26,7 @@ class InfluxDb1Container(InfluxDbContainer): .. doctest:: - >>> from testcontainers.influxdb1 import InfluxDbContainer + >>> from testcontainers.community.influxdb1 import InfluxDbContainer >>> with InfluxDbContainer() as influxdb: ... version = influxdb.get_version() diff --git a/src/testcontainers/community/influxdb2/__init__.py b/src/testcontainers/community/influxdb2/__init__.py index bd33d7fc4..1dbb83676 100644 --- a/src/testcontainers/community/influxdb2/__init__.py +++ b/src/testcontainers/community/influxdb2/__init__.py @@ -15,8 +15,7 @@ from typing import Optional from influxdb_client import InfluxDBClient, Organization - -from testcontainers.influxdb import InfluxDbContainer +from testcontainers.community.influxdb import InfluxDbContainer class InfluxDb2Container(InfluxDbContainer): @@ -28,7 +27,7 @@ class InfluxDb2Container(InfluxDbContainer): .. doctest:: - >>> from testcontainers.influxdb2 import InfluxDb2Container + >>> from testcontainers.community.influxdb2 import InfluxDb2Container >>> with InfluxDb2Container() as influxdb2: ... version = influxdb2.get_version() diff --git a/src/testcontainers/community/k3s/__init__.py b/src/testcontainers/community/k3s/__init__.py index fbdeefee3..8d1216fc4 100644 --- a/src/testcontainers/community/k3s/__init__.py +++ b/src/testcontainers/community/k3s/__init__.py @@ -27,7 +27,7 @@ class K3SContainer(DockerContainer): .. doctest:: >>> import yaml - >>> from testcontainers.k3s import K3SContainer + >>> from testcontainers.community.k3s import K3SContainer >>> from kubernetes import client, config >>> with K3SContainer() as k3s: diff --git a/src/testcontainers/community/kafka/__init__.py b/src/testcontainers/community/kafka/__init__.py index 315c16ce5..ce5532ff0 100644 --- a/src/testcontainers/community/kafka/__init__.py +++ b/src/testcontainers/community/kafka/__init__.py @@ -6,13 +6,12 @@ from os import environ from textwrap import dedent -from typing_extensions import Self - +from testcontainers.community.kafka._redpanda import RedpandaContainer from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.version import ComparableVersion from testcontainers.core.wait_strategies import LogMessageWaitStrategy -from testcontainers.kafka._redpanda import RedpandaContainer +from typing_extensions import Self __all__ = [ "KafkaContainer", @@ -42,7 +41,7 @@ class KafkaContainer(DockerContainer): .. doctest:: - >>> from testcontainers.kafka import KafkaContainer + >>> from testcontainers.community.kafka import KafkaContainer >>> with KafkaContainer() as kafka: ... connection = kafka.get_bootstrap_server() diff --git a/src/testcontainers/community/kafka/_redpanda.py b/src/testcontainers/community/kafka/_redpanda.py index a8adc0e03..d5d599670 100644 --- a/src/testcontainers/community/kafka/_redpanda.py +++ b/src/testcontainers/community/kafka/_redpanda.py @@ -17,7 +17,7 @@ class RedpandaContainer(DockerContainer): .. doctest:: - >>> from testcontainers.kafka import RedpandaContainer + >>> from testcontainers.community.kafka import RedpandaContainer >>> with RedpandaContainer() as redpanda: ... connection = redpanda.get_bootstrap_server() diff --git a/src/testcontainers/community/keycloak/__init__.py b/src/testcontainers/community/keycloak/__init__.py index 044ba0b2b..459e4ea26 100644 --- a/src/testcontainers/community/keycloak/__init__.py +++ b/src/testcontainers/community/keycloak/__init__.py @@ -14,11 +14,11 @@ from typing import Optional import requests - -from keycloak import KeycloakAdmin from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs +from keycloak import KeycloakAdmin + _DEFAULT_DEV_COMMAND = "start-dev" # Since Keycloak v26.0.0 # See: https://www.keycloak.org/server/all-config#category-bootstrap_admin @@ -36,7 +36,7 @@ class KeycloakContainer(DockerContainer): .. doctest:: - >>> from testcontainers.keycloak import KeycloakContainer + >>> from testcontainers.community.keycloak import KeycloakContainer >>> with KeycloakContainer(f"quay.io/keycloak/keycloak:25.0.4") as keycloak: ... keycloak.get_client().users_count() diff --git a/src/testcontainers/community/localstack/__init__.py b/src/testcontainers/community/localstack/__init__.py index 15cabeab6..77bc9512f 100644 --- a/src/testcontainers/community/localstack/__init__.py +++ b/src/testcontainers/community/localstack/__init__.py @@ -15,7 +15,6 @@ from typing import Any, Optional import boto3 - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -28,7 +27,7 @@ class LocalStackContainer(DockerContainer): .. doctest:: - >>> from testcontainers.localstack import LocalStackContainer + >>> from testcontainers.community.localstack import LocalStackContainer >>> with LocalStackContainer(image="localstack/localstack:2.0.1") as localstack: ... dynamo_client = localstack.get_client("dynamodb") diff --git a/src/testcontainers/community/mailpit/__init__.py b/src/testcontainers/community/mailpit/__init__.py index 466755949..a95337719 100644 --- a/src/testcontainers/community/mailpit/__init__.py +++ b/src/testcontainers/community/mailpit/__init__.py @@ -24,7 +24,6 @@ NoEncryption, ) from cryptography.x509.oid import NameOID - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -44,7 +43,7 @@ class MailpitUser(NamedTuple): .. doctest:: - >>> from testcontainers.mailpit import MailpitUser + >>> from testcontainers.community.mailpit import MailpitUser >>> users = [ ... MailpitUser("jane", "secret"), @@ -86,7 +85,7 @@ class MailpitContainer(DockerContainer): >>> import smtplib - >>> from testcontainers.mailpit import MailpitContainer + >>> from testcontainers.community.mailpit import MailpitContainer >>> with MailpitContainer() as mailpit_container: ... host_ip = mailpit_container.get_container_host_ip() @@ -105,7 +104,7 @@ class MailpitContainer(DockerContainer): >>> import smtplib - >>> from testcontainers.mailpit import MailpitContainer, MailpitUser + >>> from testcontainers.community.mailpit import MailpitContainer, MailpitUser >>> users = [MailpitUser("jane", "secret"), MailpitUser("ron", "pass2")] diff --git a/src/testcontainers/community/memcached/__init__.py b/src/testcontainers/community/memcached/__init__.py index 6da409e06..75844873d 100644 --- a/src/testcontainers/community/memcached/__init__.py +++ b/src/testcontainers/community/memcached/__init__.py @@ -28,7 +28,7 @@ class MemcachedContainer(DockerContainer): .. doctest:: - >>> from testcontainers.memcached import MemcachedContainer + >>> from testcontainers.community.memcached import MemcachedContainer >>> with MemcachedContainer() as memcached_container: ... host, port = memcached_container.get_host_and_port() diff --git a/src/testcontainers/community/milvus/__init__.py b/src/testcontainers/community/milvus/__init__.py index 2a1534146..b75b12d0c 100644 --- a/src/testcontainers/community/milvus/__init__.py +++ b/src/testcontainers/community/milvus/__init__.py @@ -12,7 +12,6 @@ # under the License. import requests - from testcontainers.core.config import testcontainers_config as c from testcontainers.core.generic import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs @@ -30,7 +29,7 @@ class MilvusContainer(DockerContainer): .. doctest:: - >>> from testcontainers.milvus import MilvusContainer + >>> from testcontainers.community.milvus import MilvusContainer >>> with MilvusContainer("milvusdb/milvus:v2.4.4") as milvus_container: ... milvus_container.get_exposed_port(milvus_container.port) in milvus_container.get_connection_url() True diff --git a/src/testcontainers/community/minio/__init__.py b/src/testcontainers/community/minio/__init__.py index 4c26648bc..63448f712 100644 --- a/src/testcontainers/community/minio/__init__.py +++ b/src/testcontainers/community/minio/__init__.py @@ -1,8 +1,9 @@ -from minio import Minio from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import HttpWaitStrategy +from minio import Minio + class MinioContainer(DockerContainer): """ @@ -18,7 +19,7 @@ class MinioContainer(DockerContainer): .. doctest:: >>> import io - >>> from testcontainers.minio import MinioContainer + >>> from testcontainers.community.minio import MinioContainer >>> with MinioContainer() as minio: ... client = minio.get_client() diff --git a/src/testcontainers/community/mongodb/__init__.py b/src/testcontainers/community/mongodb/__init__.py index 43b3fb9d8..eb7a8747d 100644 --- a/src/testcontainers/community/mongodb/__init__.py +++ b/src/testcontainers/community/mongodb/__init__.py @@ -15,11 +15,11 @@ from typing import Optional from pymongo import MongoClient - from testcontainers.core.generic import DbContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import HealthcheckWaitStrategy, LogMessageWaitStrategy + class MongoDbContainer(DbContainer): """ Mongo document-based database container. @@ -28,7 +28,7 @@ class MongoDbContainer(DbContainer): .. doctest:: - >>> from testcontainers.mongodb import MongoDbContainer + >>> from testcontainers.community.mongodb import MongoDbContainer >>> with MongoDbContainer("mongo:7.0.7") as mongo: ... db = mongo.get_connection_client().test @@ -58,9 +58,7 @@ def __init__( raise_for_deprecated_parameter(kwargs, "port_to_expose", "port") super().__init__( image=image, - _wait_strategy=LogMessageWaitStrategy( - re.compile(r"waiting for connections", re.IGNORECASE) - ), + _wait_strategy=LogMessageWaitStrategy(re.compile(r"waiting for connections", re.IGNORECASE)), **kwargs, ) self.username = username if username else os.environ.get("MONGO_INITDB_ROOT_USERNAME", "test") @@ -100,7 +98,7 @@ class MongoDBAtlasLocalContainer(DbContainer): .. doctest:: - >>> from testcontainers.mongodb import MongoDBAtlasLocalContainer + >>> from testcontainers.community.mongodb import MongoDBAtlasLocalContainer >>> import time >>> with MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8.0.13") as mongo: ... db = mongo.get_connection_client().test diff --git a/src/testcontainers/community/mqtt/__init__.py b/src/testcontainers/community/mqtt/__init__.py index 8321d0d06..e20c8adcc 100644 --- a/src/testcontainers/community/mqtt/__init__.py +++ b/src/testcontainers/community/mqtt/__init__.py @@ -14,10 +14,9 @@ from pathlib import Path from typing import TYPE_CHECKING, Optional -from typing_extensions import Self - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs +from typing_extensions import Self if TYPE_CHECKING: from paho.mqtt.client import Client @@ -31,7 +30,7 @@ class MosquittoContainer(DockerContainer): .. doctest:: - >>> from testcontainers.mqtt import MosquittoContainer + >>> from testcontainers.community.mqtt import MosquittoContainer >>> with MosquittoContainer() as mosquitto_broker: ... mqtt_client = mosquitto_broker.get_client() diff --git a/src/testcontainers/community/mssql/__init__.py b/src/testcontainers/community/mssql/__init__.py index 57f94ae55..f2b6c527c 100644 --- a/src/testcontainers/community/mssql/__init__.py +++ b/src/testcontainers/community/mssql/__init__.py @@ -15,7 +15,7 @@ class SqlServerContainer(DbContainer): .. doctest:: >>> import sqlalchemy - >>> from testcontainers.mssql import SqlServerContainer + >>> from testcontainers.community.mssql import SqlServerContainer >>> with SqlServerContainer("mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04") as mssql: ... engine = sqlalchemy.create_engine(mssql.get_connection_url()) diff --git a/src/testcontainers/community/mysql/__init__.py b/src/testcontainers/community/mysql/__init__.py index 0dba8d590..e6a37ebb4 100644 --- a/src/testcontainers/community/mysql/__init__.py +++ b/src/testcontainers/community/mysql/__init__.py @@ -36,7 +36,7 @@ class MySqlContainer(DbContainer): .. doctest:: >>> import sqlalchemy - >>> from testcontainers.mysql import MySqlContainer + >>> from testcontainers.community.mysql import MySqlContainer >>> with MySqlContainer("mysql:5.7.17", dialect="pymysql") as mysql: ... engine = sqlalchemy.create_engine(mysql.get_connection_url()) @@ -51,7 +51,7 @@ class MySqlContainer(DbContainer): .. doctest:: >>> import sqlalchemy - >>> from testcontainers.mysql import MySqlContainer + >>> from testcontainers.community.mysql import MySqlContainer >>> with MySqlContainer(seed="../../tests/seeds/") as mysql: ... engine = sqlalchemy.create_engine(mysql.get_connection_url()) ... with engine.begin() as connection: diff --git a/src/testcontainers/community/nats/__init__.py b/src/testcontainers/community/nats/__init__.py index a900036dc..071dbcce5 100644 --- a/src/testcontainers/community/nats/__init__.py +++ b/src/testcontainers/community/nats/__init__.py @@ -26,7 +26,7 @@ class NatsContainer(DockerContainer): >>> import asyncio >>> from nats import connect as nats_connect - >>> from testcontainers.nats import NatsContainer + >>> from testcontainers.community.nats import NatsContainer >>> async def test_doctest_usage(): ... with NatsContainer() as nats_container: diff --git a/src/testcontainers/community/neo4j/__init__.py b/src/testcontainers/community/neo4j/__init__.py index 4ac3387ac..b322e04ca 100644 --- a/src/testcontainers/community/neo4j/__init__.py +++ b/src/testcontainers/community/neo4j/__init__.py @@ -14,12 +14,14 @@ import os from typing import Optional -from neo4j import Driver, GraphDatabase from testcontainers.core.config import testcontainers_config as c from testcontainers.core.generic import DbContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import LogMessageWaitStrategy +from neo4j import Driver, GraphDatabase + + class Neo4jContainer(DbContainer): """ Neo4j Graph Database (Standalone) database container. @@ -28,7 +30,7 @@ class Neo4jContainer(DbContainer): .. doctest:: - >>> from testcontainers.neo4j import Neo4jContainer + >>> from testcontainers.community.neo4j import Neo4jContainer >>> with Neo4jContainer() as neo4j, \\ ... neo4j.get_driver() as driver, \\ diff --git a/src/testcontainers/community/ollama/__init__.py b/src/testcontainers/community/ollama/__init__.py index 002b02d61..2093f00da 100644 --- a/src/testcontainers/community/ollama/__init__.py +++ b/src/testcontainers/community/ollama/__init__.py @@ -16,7 +16,6 @@ from docker.types.containers import DeviceRequest from requests import get - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -44,7 +43,7 @@ class OllamaContainer(DockerContainer): .. doctest:: - >>> from testcontainers.ollama import OllamaContainer + >>> from testcontainers.community.ollama import OllamaContainer >>> with OllamaContainer() as ollama: ... ollama.list_models() [] @@ -54,7 +53,7 @@ class OllamaContainer(DockerContainer): >>> from json import loads >>> from pathlib import Path >>> from requests import post - >>> from testcontainers.ollama import OllamaContainer + >>> from testcontainers.community.ollama import OllamaContainer >>> def split_by_line(generator): ... data = b'' ... for each_item in generator: diff --git a/src/testcontainers/community/openfga/__init__.py b/src/testcontainers/community/openfga/__init__.py index 56192aebb..ac0a4ad7c 100644 --- a/src/testcontainers/community/openfga/__init__.py +++ b/src/testcontainers/community/openfga/__init__.py @@ -14,7 +14,6 @@ from typing import Optional import requests - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready @@ -41,7 +40,7 @@ class OpenFGAContainer(DockerContainer): .. doctest:: - >>> from testcontainers.openfga import OpenFGAContainer + >>> from testcontainers.community.openfga import OpenFGAContainer >>> from sys import version_info >>> with OpenFGAContainer("openfga/openfga:v1.8.4") as openfga: diff --git a/src/testcontainers/community/opensearch/__init__.py b/src/testcontainers/community/opensearch/__init__.py index 736bd98b9..c1a4b3df3 100644 --- a/src/testcontainers/community/opensearch/__init__.py +++ b/src/testcontainers/community/opensearch/__init__.py @@ -2,11 +2,10 @@ from opensearchpy import OpenSearch from opensearchpy.exceptions import ConnectionError, TransportError -from urllib3.exceptions import ProtocolError - from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import wait_container_is_ready +from urllib3.exceptions import ProtocolError MIN_REQUIRED_INITIAL_ADMIN_PASSWORD = [2, 12, 0] @@ -25,7 +24,7 @@ class OpenSearchContainer(DockerContainer): .. doctest:: - >>> from testcontainers.opensearch import OpenSearchContainer + >>> from testcontainers.community.opensearch import OpenSearchContainer >>> with OpenSearchContainer() as opensearch: ... client = opensearch.get_client() diff --git a/src/testcontainers/community/oracle/__init__.py b/src/testcontainers/community/oracle/__init__.py index 781be4280..3c53b9f62 100644 --- a/src/testcontainers/community/oracle/__init__.py +++ b/src/testcontainers/community/oracle/__init__.py @@ -19,7 +19,7 @@ class OracleDbContainer(DbContainer): ... pytest.skip("linux only test") >>> import sqlalchemy - >>> from testcontainers.oracle import OracleDbContainer + >>> from testcontainers.community.oracle import OracleDbContainer >>> with OracleDbContainer() as oracle: ... engine = sqlalchemy.create_engine(oracle.get_connection_url()) diff --git a/src/testcontainers/community/postgres/__init__.py b/src/testcontainers/community/postgres/__init__.py index add7a142a..1de6e09d5 100644 --- a/src/testcontainers/community/postgres/__init__.py +++ b/src/testcontainers/community/postgres/__init__.py @@ -33,7 +33,7 @@ class PostgresContainer(DbContainer): .. doctest:: - >>> from testcontainers.postgres import PostgresContainer + >>> from testcontainers.community.postgres import PostgresContainer >>> import sqlalchemy >>> with PostgresContainer("postgres:16") as postgres: diff --git a/src/testcontainers/community/qdrant/__init__.py b/src/testcontainers/community/qdrant/__init__.py index b2a2e8df2..828289910 100644 --- a/src/testcontainers/community/qdrant/__init__.py +++ b/src/testcontainers/community/qdrant/__init__.py @@ -26,7 +26,7 @@ class QdrantContainer(DockerContainer): Example: .. doctest:: - >>> from testcontainers.qdrant import QdrantContainer + >>> from testcontainers.community.qdrant import QdrantContainer >>> with QdrantContainer() as qdrant: ... client = qdrant.get_client() diff --git a/src/testcontainers/community/rabbitmq/__init__.py b/src/testcontainers/community/rabbitmq/__init__.py index 4a1911f3d..d86de1214 100644 --- a/src/testcontainers/community/rabbitmq/__init__.py +++ b/src/testcontainers/community/rabbitmq/__init__.py @@ -2,7 +2,6 @@ from typing import Optional import pika - from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready @@ -18,7 +17,7 @@ class RabbitMqContainer(DockerContainer): .. doctest:: >>> import pika - >>> from testcontainers.rabbitmq import RabbitMqContainer + >>> from testcontainers.community.rabbitmq import RabbitMqContainer >>> with RabbitMqContainer("rabbitmq:3.9.10") as rabbitmq: ... connection = pika.BlockingConnection(rabbitmq.get_connection_params()) diff --git a/src/testcontainers/community/redis/__init__.py b/src/testcontainers/community/redis/__init__.py index 24895b328..0cfd9be56 100644 --- a/src/testcontainers/community/redis/__init__.py +++ b/src/testcontainers/community/redis/__init__.py @@ -13,12 +13,13 @@ from typing import Optional -import redis -from redis.asyncio import Redis as asyncRedis from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import WaitStrategy, WaitStrategyTarget +import redis +from redis.asyncio import Redis as asyncRedis + class RedisContainer(DockerContainer): """ @@ -28,7 +29,7 @@ class RedisContainer(DockerContainer): .. doctest:: - >>> from testcontainers.redis import RedisContainer + >>> from testcontainers.community.redis import RedisContainer >>> with RedisContainer() as redis_container: ... redis_client = redis_container.get_client() @@ -79,7 +80,7 @@ class AsyncRedisContainer(RedisContainer): ------- .. doctest:: - >>> from testcontainers.redis import AsyncRedisContainer + >>> from testcontainers.community.redis import AsyncRedisContainer >>> with AsyncRedisContainer() as redis_container: ... redis_client =await redis_container.get_async_client() diff --git a/src/testcontainers/community/scylla/__init__.py b/src/testcontainers/community/scylla/__init__.py index 6d79ec165..ca634fd62 100644 --- a/src/testcontainers/community/scylla/__init__.py +++ b/src/testcontainers/community/scylla/__init__.py @@ -10,7 +10,7 @@ class ScyllaContainer(DockerContainer): ------- .. doctest:: - >>> from testcontainers.scylla import ScyllaContainer + >>> from testcontainers.community.scylla import ScyllaContainer >>> with ScyllaContainer() as scylla: ... cluster = scylla.get_cluster() diff --git a/src/testcontainers/community/selenium/__init__.py b/src/testcontainers/community/selenium/__init__.py index 53305ef93..2403cb7b6 100644 --- a/src/testcontainers/community/selenium/__init__.py +++ b/src/testcontainers/community/selenium/__init__.py @@ -14,14 +14,14 @@ from typing import Any, Optional import urllib3 +from testcontainers.community.selenium.video import SeleniumVideoContainer +from testcontainers.core.container import DockerContainer +from testcontainers.core.network import Network +from testcontainers.core.waiting_utils import wait_container_is_ready from typing_extensions import Self from selenium import webdriver from selenium.webdriver.common.options import ArgOptions -from testcontainers.core.container import DockerContainer -from testcontainers.core.network import Network -from testcontainers.core.waiting_utils import wait_container_is_ready -from testcontainers.selenium.video import SeleniumVideoContainer IMAGES = {"firefox": "selenium/standalone-firefox:latest", "chrome": "selenium/standalone-chrome:latest"} @@ -38,7 +38,7 @@ class BrowserWebDriverContainer(DockerContainer): .. doctest:: - >>> from testcontainers.selenium import BrowserWebDriverContainer + >>> from testcontainers.community.selenium import BrowserWebDriverContainer >>> from selenium.webdriver import DesiredCapabilities >>> with BrowserWebDriverContainer(DesiredCapabilities.CHROME) as chrome: diff --git a/src/testcontainers/community/sftp/__init__.py b/src/testcontainers/community/sftp/__init__.py index 1f39b9b83..a3c46f9f1 100644 --- a/src/testcontainers/community/sftp/__init__.py +++ b/src/testcontainers/community/sftp/__init__.py @@ -18,7 +18,6 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa - from testcontainers.core.container import DockerContainer from testcontainers.core.wait_strategies import LogMessageWaitStrategy @@ -59,7 +58,7 @@ class SFTPUser: .. doctest:: - >>> from testcontainers.sftp import SFTPUser + >>> from testcontainers.community.sftp import SFTPUser >>> users = [ ... SFTPUser("jane", password="secret"), @@ -192,7 +191,7 @@ class SFTPContainer(DockerContainer): >>> import paramiko - >>> from testcontainers.sftp import SFTPContainer + >>> from testcontainers.community.sftp import SFTPContainer >>> with SFTPContainer() as sftp_container: ... host_ip = sftp_container.get_container_host_ip() @@ -211,7 +210,7 @@ class SFTPContainer(DockerContainer): >>> import paramiko - >>> from testcontainers.sftp import SFTPContainer + >>> from testcontainers.community.sftp import SFTPContainer >>> with SFTPContainer() as sftp_container: ... host_ip = sftp_container.get_container_host_ip() diff --git a/src/testcontainers/community/vault/__init__.py b/src/testcontainers/community/vault/__init__.py index c89b27ea5..7b8cb4e54 100644 --- a/src/testcontainers/community/vault/__init__.py +++ b/src/testcontainers/community/vault/__init__.py @@ -27,7 +27,7 @@ class VaultContainer(DockerContainer): .. doctest:: - >>> from testcontainers.vault import VaultContainer + >>> from testcontainers.community.vault import VaultContainer >>> import hvac >>> with VaultContainer("hashicorp/vault:1.16.1") as vault_container: diff --git a/src/testcontainers/community/weaviate/__init__.py b/src/testcontainers/community/weaviate/__init__.py index c33983121..6049aaee6 100644 --- a/src/testcontainers/community/weaviate/__init__.py +++ b/src/testcontainers/community/weaviate/__init__.py @@ -13,7 +13,6 @@ from typing import TYPE_CHECKING, Optional from requests import ConnectionError, get - from testcontainers.core.generic import DbContainer from testcontainers.core.waiting_utils import wait_container_is_ready @@ -36,7 +35,7 @@ class WeaviateContainer(DbContainer): .. doctest:: - >>> from testcontainers.weaviate import WeaviateContainer + >>> from testcontainers.community.weaviate import WeaviateContainer >>> with WeaviateContainer() as container: ... with container.get_client() as client: @@ -47,7 +46,7 @@ class WeaviateContainer(DbContainer): .. doctest:: - >>> from testcontainers.weaviate import WeaviateContainer + >>> from testcontainers.community.weaviate import WeaviateContainer >>> with WeaviateContainer( ... env_vars={ diff --git a/src/testcontainers/core/generic.py b/src/testcontainers/core/generic.py index 1410321ee..c19d39143 100644 --- a/src/testcontainers/core/generic.py +++ b/src/testcontainers/core/generic.py @@ -30,7 +30,7 @@ class DbContainer(DockerContainer): """ **DEPRECATED (for removal)** Please use database-specific container classes or `SqlContainer` instead. - # from testcontainers.generic.sql import SqlContainer + # from testcontainers.community.generic.sql import SqlContainer Generic database container. """ diff --git a/tests/community/generic/test_server.py b/tests/community/generic/test_server.py index 5943b4a4d..81f4e1dd1 100644 --- a/tests/community/generic/test_server.py +++ b/tests/community/generic/test_server.py @@ -7,7 +7,7 @@ from testcontainers.core.waiting_utils import wait_for_logs from testcontainers.core.image import DockerImage -from testcontainers.generic import ServerContainer +from testcontainers.community.generic import ServerContainer TEST_DIR = Path(__file__).parent diff --git a/tests/community/generic/test_sql.py b/tests/community/generic/test_sql.py index 69fff2427..7aade4952 100644 --- a/tests/community/generic/test_sql.py +++ b/tests/community/generic/test_sql.py @@ -2,8 +2,8 @@ from unittest.mock import patch from testcontainers.core.exceptions import ContainerStartException -from testcontainers.generic.sql import SqlContainer -from testcontainers.generic.providers.sql_connection_wait_strategy import SqlAlchemyConnectWaitStrategy +from testcontainers.community.generic.sql import SqlContainer +from testcontainers.community.generic.providers.sql_connection_wait_strategy import SqlAlchemyConnectWaitStrategy class SimpleSqlContainer(SqlContainer): @@ -153,7 +153,7 @@ def test_container_inheritance(self): assert hasattr(container, "start") def test_additional_transient_errors_list(self): - from testcontainers.generic.providers.sql_connection_wait_strategy import ADDITIONAL_TRANSIENT_ERRORS + from testcontainers.community.generic.providers.sql_connection_wait_strategy import ADDITIONAL_TRANSIENT_ERRORS assert isinstance(ADDITIONAL_TRANSIENT_ERRORS, list) # List may be empty if SQLAlchemy not available, or contain DBAPIError if it is From 121b62b477f607284c1b51636a96668bd33d7a89 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 00:54:34 +0200 Subject: [PATCH 03/29] fix(docs): update docs reflecting new structure --- .github/CONTRIBUTING.md | 2 +- .github/PULL_REQUEST_TEMPLATE/new_container.md | 12 ++++-------- docs/community/generic.rst | 4 ++-- docs/contributing.md | 6 ++++-- docs/index.rst | 3 ++- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 1a12af908..f0e2346dd 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -31,7 +31,7 @@ You need to have the following tools available to you: - Run `make install` to get `uv` to install all dependencies and set up `pre-commit` - **Recommended**: Run `make` or `make help` to see other commands available to you. - After this, you should have a working virtual environment and proceed with writing code with your favourite IDE -- **TIP**: You can run `make core/tests` or `make modules//tests` to run the tests specifically for that to speed up feedback cycles +- **TIP**: You can run `make core/tests` or `make community//tests` to run the tests specifically for that to speed up feedback cycles - You can also run `make lint` to run the `pre-commit` for the entire codebase. diff --git a/.github/PULL_REQUEST_TEMPLATE/new_container.md b/.github/PULL_REQUEST_TEMPLATE/new_container.md index 0d790fc49..5b028dc07 100644 --- a/.github/PULL_REQUEST_TEMPLATE/new_container.md +++ b/.github/PULL_REQUEST_TEMPLATE/new_container.md @@ -20,16 +20,12 @@ It helps reduce unnecessary work for you and the maintainers! So please use `fix(postgres):` or `fix(my_new_vector_db):` if you want to add or modify community modules. This may change in the future if we have a separate package released with community modules. - [ ] Your PR allows maintainers to edit your branch, this will speed up resolving minor issues! -- [ ] The new container is implemented under `modules/*` - - Your module follows [PEP 420](https://peps.python.org/pep-0420/) with implicit namespace packages - (if unsure, look at other existing community modules) - - Your package namespacing follows `testcontainers..*` - and you DO NOT have an `__init__.py` above your module's level. - - Your module has its own tests under `modules/*/tests` - - Your module has a `README.rst` and hooks in the `.. auto-class` and `.. title` of your container +- [ ] The new container is implemented under `src/testcontainers/community//` + - Your package namespacing follows `testcontainers.community..*`. + - Your module has its own tests under `tests/community//` + - Your module has a `docs/community/.rst` with the `.. autoclass` and `.. title` of your container - Implement the new feature (typically in `__init__.py`) and corresponding tests. - [ ] Your module is added in `pyproject.toml` - - it is declared under `tool.hatch.build.targets.wheel` - see other community modules - it is declared under `project.optional-dependencies` with the same name as your module name, we still prefer adding _NO EXTRA DEPENDENCIES_, meaning `mymodule = []` is the preferred addition (see the notes at the bottom) diff --git a/docs/community/generic.rst b/docs/community/generic.rst index 5d062b5db..5bec0092c 100644 --- a/docs/community/generic.rst +++ b/docs/community/generic.rst @@ -11,7 +11,7 @@ FastAPI container that is using :code:`ServerContainer` >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./modules/generic/tests/samples/fastapi", tag="fastapi-test:latest") as image: + >>> with DockerImage(path="./tests/community/generic/samples/fastapi", tag="fastapi-test:latest") as image: ... with ServerContainer(port=80, image=image) as fastapi_server: ... delay = wait_for_logs(fastapi_server, "Uvicorn running on http://0.0.0.0:80") ... fastapi_server.get_api_url = lambda: fastapi_server._create_connection_url() + "/api/v1/" @@ -31,7 +31,7 @@ A more advance use-case, where we are using a FastAPI container that is using Re ... redis_container_port = redis.port ... redis_container_ip_address = redis.get_docker_client().bridge_ip(redis._container.id) - ... with DockerImage(path="./modules/generic/tests/samples/advance_1", tag="advance-1:latest") as image: + ... with DockerImage(path="./tests/community/generic/samples/advance_1", tag="advance-1:latest") as image: ... web_server = ServerContainer(port=80, image=image) ... web_server.with_env(key="REDIS_HOST", value=redis_container_ip_address) ... web_server.with_env(key="REDIS_PORT", value=redis_container_port) diff --git a/docs/contributing.md b/docs/contributing.md index 0ad083e2d..ebba8cd41 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -30,7 +30,7 @@ You need to have the following tools available to you: - Run `make install` to get `uv` to install all dependencies and set up `pre-commit` - **Recommended**: Run `make` or `make help` to see other commands available to you. - After this, you should have a working virtual environment and proceed with writing code with your favorite IDE -- **TIP**: You can run `make core/tests` or `make modules//tests` to run the tests specifically for that to speed up feedback cycles +- **TIP**: You can run `make core/tests` or `make community//tests` to run the tests specifically for that to speed up feedback cycles - You can also run `make lint` to run the `pre-commit` for the entire codebase. ## Adding new modules @@ -47,10 +47,12 @@ Once you've talked to the maintainers (we do our best to reply!) then you can pr ### Module documentation -Leave examples for others with your mew module such as `modules//basic_example.py`. You can create as many examples as you want. +Leave examples for others with your new module such as `docs/modules/_example.py`. You can create as many examples as you want. Create a new `docs/modules/.md` describing the basic use of the new container. There is a [starter template provided here](https://raw.githubusercontent.com/testcontainers/testcontainers-python/blob/main/docs/modules/template.md){:target="\_blank"}. +Also create a `docs/community/.rst` with the `.. autoclass` and `.. title` directives pointing at your container class — this is used by the Sphinx doctest runner. + !!! important Make sure to add your new module to the sidebar nav in the `mkdocs.yml` diff --git a/docs/index.rst b/docs/index.rst index b906e3ed9..613e903c9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,7 @@ testcontainers-python facilitates the use of Docker containers for functional an .. toctree:: :maxdepth: 1 + :glob: core/README compose @@ -78,7 +79,7 @@ Crafting containers that are based on custom images is supported by the `core` m This allows you to create containers from images that are not part of the modules provided by testcontainers-python. -For common use cases, you can also use the generic containers provided by the `testcontainers-generic` module. Please check the `generic documentation `_ for more information. +For common use cases, you can also use the generic containers provided by the `testcontainers-generic` module. Please check the `generic documentation `_ for more information. (example: `ServerContainer` for running a FastAPI server) From 845ca971debbb37fc02a66a13d10abb372aadb00 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 01:26:14 +0200 Subject: [PATCH 04/29] fix(main): adopt ci to new src structure - removed some now deprecated make entrypoints/scripts --- .github/workflows/ci-community.yml | 28 ++++++++++++++---- .github/workflows/ci-lint.yml | 10 +------ Makefile | 47 +++++++++++++++--------------- scripts/mypy_report.py | 35 ---------------------- 4 files changed, 46 insertions(+), 74 deletions(-) delete mode 100755 scripts/mypy_report.py diff --git a/.github/workflows/ci-community.yml b/.github/workflows/ci-community.yml index ed36b71c2..a43a2661d 100644 --- a/.github/workflows/ci-community.yml +++ b/.github/workflows/ci-community.yml @@ -10,11 +10,13 @@ on: push: branches: [ main ] paths: - - "modules/**" + - "src/testcontainers/community/**" + - "tests/community/**" pull_request: branches: [ main ] paths: - - "modules/**" + - "src/testcontainers/community/**" + - "tests/community/**" jobs: track-modules: @@ -32,15 +34,29 @@ jobs: list-files: 'json' filters: | modules: - - 'modules/**' + - 'src/testcontainers/community/**' + - 'tests/community/**' - name: Compute modules from files id: compute-changes run: | - modules=$(echo "${{ toJson(steps.changed-files.outputs.modules_files) }}" | jq '.[] | split("/") | nth(1)' | jq -s -c '. | unique') + modules=$(echo '${{ toJson(steps.changed-files.outputs.modules_files) }}' | jq -c ' + [.[] | + if startswith("src/testcontainers/community/") then split("/")[3] + elif startswith("tests/community/") then split("/")[2] + else empty + end | + select(. and (startswith("__") | not)) | + if . == "oracle" then "oracle-free" + elif . == "influxdb1" or . == "influxdb2" then "influxdb" + else . + end + ] | unique + ') echo "computed_modules=$modules" echo "computed_modules=$modules" >> $GITHUB_OUTPUT outputs: changed_modules: ${{ steps.compute-changes.outputs.computed_modules }} + test: runs-on: ubuntu-22.04 needs: [track-modules] @@ -60,6 +76,6 @@ jobs: - name: Install Python dependencies run: uv sync --extra ${{ matrix.module }} - name: Run tests - run: make modules/${{ matrix.module }}/tests + run: make community/${{ matrix.module }}/tests - name: Run doctests - run: make modules/${{ matrix.module }}/doctests + run: make community/${{ matrix.module }}/doctests diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index 73c9e8f15..f87028dc0 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -22,12 +22,4 @@ jobs: - name: Execute pre-commit handler continue-on-error: true run: | - uv run pre-commit run check-toml - uv run pre-commit run trailing-whitespace - uv run pre-commit run end-of-file-fixer - uv run pre-commit run ruff - uv run pre-commit run ruff-format - - name: Execute mypy - run: | - make mypy-core-report - make mypy-core + uv run pre-commit run --all-files diff --git a/Makefile b/Makefile index 46907cd25..50540308c 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,10 @@ PYTHON_VERSION ?= 3.10 IMAGE = testcontainers-python:${PYTHON_VERSION} -PACKAGES = core $(addprefix modules/,$(notdir $(wildcard modules/*))) -UPLOAD = $(addsuffix /upload,${PACKAGES}) -TESTS = $(addsuffix /tests,$(filter-out meta,${PACKAGES})) -TESTS_DIND = $(addsuffix -dind,${TESTS}) -DOCTESTS = $(addsuffix /doctests,$(filter-out modules/README.md,${PACKAGES})) +COMMUNITY_MODULES = $(patsubst tests/community/%/,%,$(wildcard tests/community/*/)) +COMMUNITY_TESTS = $(addprefix community/,$(addsuffix /tests,$(COMMUNITY_MODULES))) +COMMUNITY_DOCTESTS = $(addprefix community/,$(addsuffix /doctests,$(COMMUNITY_MODULES))) install: ## Set up the project for development @@ -18,15 +16,17 @@ install: ## Set up the project for development build: ## Build the python package uv build && uv run twine check dist/* -tests: ${TESTS} ## Run tests for each package -${TESTS}: %/tests: - uv run coverage run --parallel -m pytest -v $*/tests +tests: core/tests community-tests ## Run all tests -quick-core-tests: ## Run core tests excluding long_running - uv run coverage run --parallel -m pytest -v -m "not long_running" core/tests +core/tests: ## Run tests for the core package + uv run coverage run --parallel -m pytest -v tests/core + +community-tests: $(COMMUNITY_TESTS) ## Run tests for all community modules +$(COMMUNITY_TESTS): community/%/tests: + uv run coverage run --parallel -m pytest -v tests/community/$* -core-tests: ## Run tests for the core package - uv run coverage run --parallel -m pytest -v core/tests +quick-core-tests: ## Run core tests excluding long_running + uv run coverage run --parallel -m pytest -v -m "not long_running" tests/core coverage: ## Target to combine and report coverage. uv run coverage combine @@ -37,24 +37,21 @@ coverage: ## Target to combine and report coverage. lint: ## Lint all files in the project, which we also run in pre-commit uv run pre-commit run --all-files -mypy-core: ## Run mypy on the core package - uv run mypy --config-file pyproject.toml core - -mypy-core-report: ## Generate a report for mypy on the core package - uv run mypy --config-file pyproject.toml core | uv run python scripts/mypy_report.py - docs: ## Build the docs for the project uv run --all-extras sphinx-build -nW docs docs/_build # Target to build docs watching for changes as per https://stackoverflow.com/a/21389615 -docs-watch : +docs-watch: uv run sphinx-autobuild docs docs/_build # requires 'pip install sphinx-autobuild' -doctests: ${DOCTESTS} ## Run doctests found across the documentation. - uv run --all-extras sphinx-build -b doctest . docs/_build +doctests: core/doctests community-doctests ## Run doctests found across the documentation. + +core/doctests: ## Run doctests for the core package + uv run --all-extras sphinx-build -b doctest -c doctests docs/core docs/_build -${DOCTESTS}: %/doctests: ## Run doctests found for a module. - uv run --all-extras sphinx-build -b doctest -c doctests $* docs/_build +community-doctests: $(COMMUNITY_DOCTESTS) ## Run doctests for all community modules +$(COMMUNITY_DOCTESTS): community/%/doctests: + uv run --all-extras sphinx-build -b doctest docs docs/_build docs/community/$*.rst clean: ## Remove generated files. @@ -67,7 +64,9 @@ clean-all: clean ## Remove all generated files and reset the local virtual envir rm -rf .venv # Targets that do not generate file-level artifacts. -.PHONY: clean docs doctests image tests quick-core-tests ${TESTS} +.PHONY: clean docs doctests core/doctests community-doctests $(COMMUNITY_DOCTESTS) \ + tests core/tests community-tests $(COMMUNITY_TESTS) \ + quick-core-tests install build coverage lint mypy-core mypy-core-report docs-watch # Implements this pattern for autodocumenting Makefiles: diff --git a/scripts/mypy_report.py b/scripts/mypy_report.py deleted file mode 100755 index 7bc12474a..000000000 --- a/scripts/mypy_report.py +++ /dev/null @@ -1,35 +0,0 @@ -# Description: This script reads the output of mypy and generates a summary of errors by file. - -import re -import sys - -from rich.console import Console -from rich.table import Table - -# Regular expression to match file path and error count -pattern = r"(.*\.py:\d+):\s+error: (.*)" - -error_dict = {} - -for line in sys.stdin: - match = re.search(pattern, line) - if match: - # Extract file path and error message - file_path, _ = match.group(1).split(":") - error_message = match.group(2) - - if file_path not in error_dict: - error_dict[file_path] = 1 - else: - error_dict[file_path] += 1 - -table = Table(title="Error Summary") -table.add_column("File Path") -table.add_column("Errors", justify="left") - -for file_path, error_count in error_dict.items(): - table.add_row(file_path, str(error_count)) - -console = Console() -console.print(table) -console.print(f"[red]Found {sum(error_dict.values())} errors in {len(error_dict)} files.[/red]") From fa336e96a88a79a062e3d5d0ba82cc2c6b37acd4 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 01:30:23 +0200 Subject: [PATCH 05/29] fix(main): adopt imports to new structure --- tests/community/arangodb/test_arangodb.py | 2 +- tests/community/aws/test_aws.py | 4 ++-- tests/community/azurite/test_azurite.py | 2 +- tests/community/cassandra/test_cassandra.py | 2 +- tests/community/chroma/test_chroma.py | 2 +- tests/community/clickhouse/test_clickhouse.py | 2 +- tests/community/cockroachdb/test_cockroachdb.py | 2 +- tests/community/cosmosdb/test_cosmosdb_emulator.py | 2 +- tests/community/cosmosdb/test_cosmosdb_mongodb.py | 2 +- tests/community/cosmosdb/test_cosmosdb_nosql.py | 2 +- tests/community/db2/test_db2.py | 2 +- tests/community/elasticsearch/test_elasticsearch.py | 2 +- tests/community/google/test_google.py | 2 +- tests/community/influxdb/test_influxdb.py | 6 +++--- tests/community/k3s/test_k3s.py | 2 +- tests/community/kafka/test_kafka.py | 2 +- tests/community/kafka/test_redpanda.py | 2 +- tests/community/keycloak/test_keycloak.py | 2 +- tests/community/localstack/test_localstack.py | 4 ++-- tests/community/mailpit/test_mailpit.py | 2 +- tests/community/memcached/test_memcached.py | 2 +- tests/community/milvus/test_milvus.py | 2 +- tests/community/minio/test_minio.py | 2 +- tests/community/mongodb/test_mongodb.py | 2 +- tests/community/mqtt/test_mosquitto.py | 2 +- tests/community/mssql/test_mssql.py | 2 +- tests/community/mysql/test_mysql.py | 2 +- tests/community/nats/test_nats.py | 2 +- tests/community/nats/test_nats_jetstream.py | 2 +- tests/community/neo4j/test_neo4j.py | 2 +- tests/community/nginx/test_nginx.py | 2 +- tests/community/ollama/test_ollama.py | 2 +- tests/community/openfga/test_openfga.py | 2 +- tests/community/opensearch/test_opensearch.py | 2 +- tests/community/oracle-free/test_oracle.py | 2 +- tests/community/postgres/test_postgres.py | 2 +- tests/community/qdrant/test_qdrant.py | 2 +- tests/community/rabbitmq/test_rabbitmq.py | 2 +- tests/community/redis/test_redis.py | 2 +- tests/community/registry/test_registry.py | 2 +- tests/community/scylla/test_scylla.py | 2 +- tests/community/selenium/test_selenium.py | 2 +- tests/community/sftp/test_sftp.py | 2 +- tests/community/trino/test_trino.py | 2 +- tests/community/valkey/test_valkey.py | 2 +- tests/community/vault/test_vault.py | 2 +- tests/community/weaviate/test_weaviate.py | 2 +- 47 files changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/community/arangodb/test_arangodb.py b/tests/community/arangodb/test_arangodb.py index 01e4643a7..1a8df606e 100644 --- a/tests/community/arangodb/test_arangodb.py +++ b/tests/community/arangodb/test_arangodb.py @@ -6,7 +6,7 @@ from arango import ArangoClient from arango.exceptions import DatabaseCreateError, ServerVersionError -from testcontainers.arangodb import ArangoDbContainer +from testcontainers.community.arangodb import ArangoDbContainer import platform ARANGODB_IMAGE_NAME = "arangodb" diff --git a/tests/community/aws/test_aws.py b/tests/community/aws/test_aws.py index 873b87352..c403f5129 100644 --- a/tests/community/aws/test_aws.py +++ b/tests/community/aws/test_aws.py @@ -5,8 +5,8 @@ from unittest.mock import patch from testcontainers.core.image import DockerImage -from testcontainers.aws import AWSLambdaContainer -from testcontainers.aws.aws_lambda import RIE_PATH +from testcontainers.community.aws import AWSLambdaContainer +from testcontainers.community.aws.aws_lambda import RIE_PATH DOCKER_FILE_PATH = "./modules/aws/tests/lambda_sample" IMAGE_TAG = "lambda:test" diff --git a/tests/community/azurite/test_azurite.py b/tests/community/azurite/test_azurite.py index 2ec3c7502..03329adac 100644 --- a/tests/community/azurite/test_azurite.py +++ b/tests/community/azurite/test_azurite.py @@ -4,7 +4,7 @@ from azure.storage.blob import BlobServiceClient -from testcontainers.azurite import AzuriteContainer, ConnectionStringType +from testcontainers.community.azurite import AzuriteContainer, ConnectionStringType from testcontainers.core.image import DockerImage from testcontainers.core.container import DockerContainer diff --git a/tests/community/cassandra/test_cassandra.py b/tests/community/cassandra/test_cassandra.py index 5be9aa81a..c9558997e 100644 --- a/tests/community/cassandra/test_cassandra.py +++ b/tests/community/cassandra/test_cassandra.py @@ -1,4 +1,4 @@ -from testcontainers.cassandra import CassandraContainer +from testcontainers.community.cassandra import CassandraContainer import sys from importlib.metadata import version diff --git a/tests/community/chroma/test_chroma.py b/tests/community/chroma/test_chroma.py index 444c201df..26dc2dd0c 100644 --- a/tests/community/chroma/test_chroma.py +++ b/tests/community/chroma/test_chroma.py @@ -1,4 +1,4 @@ -from testcontainers.chroma import ChromaContainer +from testcontainers.community.chroma import ChromaContainer import chromadb diff --git a/tests/community/clickhouse/test_clickhouse.py b/tests/community/clickhouse/test_clickhouse.py index 23e5f4686..dfce769a7 100644 --- a/tests/community/clickhouse/test_clickhouse.py +++ b/tests/community/clickhouse/test_clickhouse.py @@ -1,6 +1,6 @@ import clickhouse_driver -from testcontainers.clickhouse import ClickHouseContainer +from testcontainers.community.clickhouse import ClickHouseContainer def test_docker_run_clickhouse(): diff --git a/tests/community/cockroachdb/test_cockroachdb.py b/tests/community/cockroachdb/test_cockroachdb.py index af20fd580..e1cfc0837 100644 --- a/tests/community/cockroachdb/test_cockroachdb.py +++ b/tests/community/cockroachdb/test_cockroachdb.py @@ -1,6 +1,6 @@ import sqlalchemy -from testcontainers.cockroachdb import CockroachDBContainer +from testcontainers.community.cockroachdb import CockroachDBContainer def test_docker_run_mysql(): diff --git a/tests/community/cosmosdb/test_cosmosdb_emulator.py b/tests/community/cosmosdb/test_cosmosdb_emulator.py index 542ddd11c..eb951d452 100644 --- a/tests/community/cosmosdb/test_cosmosdb_emulator.py +++ b/tests/community/cosmosdb/test_cosmosdb_emulator.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.cosmosdb._emulator import CosmosDBEmulatorContainer +from testcontainers.community.cosmosdb._emulator import CosmosDBEmulatorContainer def test_runs(): diff --git a/tests/community/cosmosdb/test_cosmosdb_mongodb.py b/tests/community/cosmosdb/test_cosmosdb_mongodb.py index a50ee82ea..2ddd4cac7 100644 --- a/tests/community/cosmosdb/test_cosmosdb_mongodb.py +++ b/tests/community/cosmosdb/test_cosmosdb_mongodb.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer +from testcontainers.community.cosmosdb import CosmosDBMongoEndpointContainer def test_requires_a_version(): diff --git a/tests/community/cosmosdb/test_cosmosdb_nosql.py b/tests/community/cosmosdb/test_cosmosdb_nosql.py index a9460a1b0..c7e418919 100644 --- a/tests/community/cosmosdb/test_cosmosdb_nosql.py +++ b/tests/community/cosmosdb/test_cosmosdb_nosql.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer +from testcontainers.community.cosmosdb import CosmosDBNoSQLEndpointContainer def test_runs(): diff --git a/tests/community/db2/test_db2.py b/tests/community/db2/test_db2.py index 7b6ea844a..6b3ebba1e 100644 --- a/tests/community/db2/test_db2.py +++ b/tests/community/db2/test_db2.py @@ -4,7 +4,7 @@ import sqlalchemy from testcontainers.core.utils import is_arm -from testcontainers.db2 import Db2Container +from testcontainers.community.db2 import Db2Container @pytest.mark.skipif(is_arm(), reason="db2 container not available for ARM") diff --git a/tests/community/elasticsearch/test_elasticsearch.py b/tests/community/elasticsearch/test_elasticsearch.py index d6666aeca..b5e384e3d 100644 --- a/tests/community/elasticsearch/test_elasticsearch.py +++ b/tests/community/elasticsearch/test_elasticsearch.py @@ -3,7 +3,7 @@ import pytest -from testcontainers.elasticsearch import ElasticSearchContainer +from testcontainers.community.elasticsearch import ElasticSearchContainer # The versions below should reflect the latest stable releases diff --git a/tests/community/google/test_google.py b/tests/community/google/test_google.py index c91793741..cdf890b80 100644 --- a/tests/community/google/test_google.py +++ b/tests/community/google/test_google.py @@ -2,7 +2,7 @@ from google.cloud.datastore import Entity from testcontainers.core.waiting_utils import wait_for_logs -from testcontainers.google import PubSubContainer, DatastoreContainer +from testcontainers.community.google import PubSubContainer, DatastoreContainer def test_pubsub_container(): diff --git a/tests/community/influxdb/test_influxdb.py b/tests/community/influxdb/test_influxdb.py index 62144a3c3..d628939b6 100644 --- a/tests/community/influxdb/test_influxdb.py +++ b/tests/community/influxdb/test_influxdb.py @@ -19,9 +19,9 @@ from influxdb_client.client.write_api import SYNCHRONOUS from pytest import mark -from testcontainers.influxdb import InfluxDbContainer -from testcontainers.influxdb1 import InfluxDb1Container -from testcontainers.influxdb2 import InfluxDb2Container +from testcontainers.community.influxdb import InfluxDbContainer +from testcontainers.community.influxdb1 import InfluxDb1Container +from testcontainers.community.influxdb2 import InfluxDb2Container @mark.parametrize( diff --git a/tests/community/k3s/test_k3s.py b/tests/community/k3s/test_k3s.py index edff1c6d8..d56338809 100644 --- a/tests/community/k3s/test_k3s.py +++ b/tests/community/k3s/test_k3s.py @@ -2,7 +2,7 @@ import yaml from kubernetes import client, config -from testcontainers.k3s import K3SContainer +from testcontainers.community.k3s import K3SContainer def test_docker_run_k3s(): diff --git a/tests/community/kafka/test_kafka.py b/tests/community/kafka/test_kafka.py index 901f3f0c3..a9651e162 100644 --- a/tests/community/kafka/test_kafka.py +++ b/tests/community/kafka/test_kafka.py @@ -2,7 +2,7 @@ from kafka import KafkaAdminClient, KafkaConsumer, KafkaProducer, TopicPartition from testcontainers.core.network import Network -from testcontainers.kafka import KafkaContainer, kafka_config +from testcontainers.community.kafka import KafkaContainer, kafka_config def test_kafka_producer_consumer(): diff --git a/tests/community/kafka/test_redpanda.py b/tests/community/kafka/test_redpanda.py index 93f108a71..e92497c82 100644 --- a/tests/community/kafka/test_redpanda.py +++ b/tests/community/kafka/test_redpanda.py @@ -5,7 +5,7 @@ from kafka import KafkaConsumer, KafkaProducer, TopicPartition, KafkaAdminClient from kafka.admin import NewTopic -from testcontainers.kafka import RedpandaContainer +from testcontainers.community.kafka import RedpandaContainer def test_redpanda_producer_consumer(): diff --git a/tests/community/keycloak/test_keycloak.py b/tests/community/keycloak/test_keycloak.py index 4df0ca9c9..1880be14e 100644 --- a/tests/community/keycloak/test_keycloak.py +++ b/tests/community/keycloak/test_keycloak.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.keycloak import KeycloakContainer +from testcontainers.community.keycloak import KeycloakContainer @pytest.mark.parametrize("image_version", ["26.4.0", "26.0.0", "25.0", "24.0.1", "18.0"]) diff --git a/tests/community/localstack/test_localstack.py b/tests/community/localstack/test_localstack.py index 6801aefdb..0243e4292 100644 --- a/tests/community/localstack/test_localstack.py +++ b/tests/community/localstack/test_localstack.py @@ -1,7 +1,7 @@ import json import urllib -from testcontainers.localstack import LocalStackContainer +from testcontainers.community.localstack import LocalStackContainer def test_docker_run_localstack(): @@ -16,7 +16,7 @@ def test_docker_run_localstack(): def test_localstack_boto3(): - from testcontainers.localstack import LocalStackContainer + from testcontainers.community.localstack import LocalStackContainer with LocalStackContainer(image="localstack/localstack:2.0.1") as localstack: dynamo_client = localstack.get_client("dynamodb") diff --git a/tests/community/mailpit/test_mailpit.py b/tests/community/mailpit/test_mailpit.py index 53247f49d..0818165dc 100644 --- a/tests/community/mailpit/test_mailpit.py +++ b/tests/community/mailpit/test_mailpit.py @@ -4,7 +4,7 @@ import pytest -from testcontainers.mailpit import MailpitContainer, MailpitUser +from testcontainers.community.mailpit import MailpitContainer, MailpitUser _sender = "from@example.com" _receivers = ["to@example.com"] diff --git a/tests/community/memcached/test_memcached.py b/tests/community/memcached/test_memcached.py index 853ede40a..d7e82092a 100644 --- a/tests/community/memcached/test_memcached.py +++ b/tests/community/memcached/test_memcached.py @@ -1,6 +1,6 @@ import socket -from testcontainers.memcached import MemcachedContainer +from testcontainers.community.memcached import MemcachedContainer import pytest diff --git a/tests/community/milvus/test_milvus.py b/tests/community/milvus/test_milvus.py index 12887a49b..7f05f95a1 100644 --- a/tests/community/milvus/test_milvus.py +++ b/tests/community/milvus/test_milvus.py @@ -1,7 +1,7 @@ import pytest from pymilvus import MilvusClient -from testcontainers.milvus import MilvusContainer +from testcontainers.community.milvus import MilvusContainer VERSIONS = ["v2.4.0", "v2.4.4"] diff --git a/tests/community/minio/test_minio.py b/tests/community/minio/test_minio.py index 99eed4380..5fa25ee90 100644 --- a/tests/community/minio/test_minio.py +++ b/tests/community/minio/test_minio.py @@ -1,6 +1,6 @@ import io -from testcontainers.minio import MinioContainer +from testcontainers.community.minio import MinioContainer def test_docker_run_minio(): diff --git a/tests/community/mongodb/test_mongodb.py b/tests/community/mongodb/test_mongodb.py index 352c4a709..284f221d2 100644 --- a/tests/community/mongodb/test_mongodb.py +++ b/tests/community/mongodb/test_mongodb.py @@ -3,7 +3,7 @@ from pymongo import MongoClient from pymongo.errors import OperationFailure -from testcontainers.mongodb import MongoDbContainer, MongoDBAtlasLocalContainer +from testcontainers.community.mongodb import MongoDbContainer, MongoDBAtlasLocalContainer @pytest.mark.parametrize("version", ["7.0.7", "6.0.14", "5.0.26"]) diff --git a/tests/community/mqtt/test_mosquitto.py b/tests/community/mqtt/test_mosquitto.py index 1e058103c..da5ad3e8e 100644 --- a/tests/community/mqtt/test_mosquitto.py +++ b/tests/community/mqtt/test_mosquitto.py @@ -1,6 +1,6 @@ import pytest -from testcontainers.mqtt import MosquittoContainer +from testcontainers.community.mqtt import MosquittoContainer VERSIONS = ["1.6.15", "2.0.18", "2.1.2-alpine"] diff --git a/tests/community/mssql/test_mssql.py b/tests/community/mssql/test_mssql.py index c8c6d5e1a..b0cb09d03 100644 --- a/tests/community/mssql/test_mssql.py +++ b/tests/community/mssql/test_mssql.py @@ -2,7 +2,7 @@ import sqlalchemy from testcontainers.core.utils import is_arm -from testcontainers.mssql import SqlServerContainer +from testcontainers.community.mssql import SqlServerContainer @pytest.mark.skipif(is_arm(), reason="mssql container not available for ARM") diff --git a/tests/community/mysql/test_mysql.py b/tests/community/mysql/test_mysql.py index a2d2c2ec9..23b66f6c4 100644 --- a/tests/community/mysql/test_mysql.py +++ b/tests/community/mysql/test_mysql.py @@ -6,7 +6,7 @@ import sqlalchemy from testcontainers.core.utils import is_arm -from testcontainers.mysql import MySqlContainer +from testcontainers.community.mysql import MySqlContainer @pytest.mark.inside_docker_check diff --git a/tests/community/nats/test_nats.py b/tests/community/nats/test_nats.py index 7b72ea81b..25236e2a8 100644 --- a/tests/community/nats/test_nats.py +++ b/tests/community/nats/test_nats.py @@ -1,4 +1,4 @@ -from testcontainers.nats import NatsContainer +from testcontainers.community.nats import NatsContainer from uuid import uuid4 import pytest diff --git a/tests/community/nats/test_nats_jetstream.py b/tests/community/nats/test_nats_jetstream.py index 368e8c36f..31e488c51 100644 --- a/tests/community/nats/test_nats_jetstream.py +++ b/tests/community/nats/test_nats_jetstream.py @@ -1,4 +1,4 @@ -from testcontainers.nats import NatsContainer +from testcontainers.community.nats import NatsContainer from uuid import uuid4 import pytest diff --git a/tests/community/neo4j/test_neo4j.py b/tests/community/neo4j/test_neo4j.py index 6058d34c2..43192ae19 100644 --- a/tests/community/neo4j/test_neo4j.py +++ b/tests/community/neo4j/test_neo4j.py @@ -1,4 +1,4 @@ -from testcontainers.neo4j import Neo4jContainer +from testcontainers.community.neo4j import Neo4jContainer def test_docker_run_neo4j_latest(): diff --git a/tests/community/nginx/test_nginx.py b/tests/community/nginx/test_nginx.py index 39fba5e97..79c781756 100644 --- a/tests/community/nginx/test_nginx.py +++ b/tests/community/nginx/test_nginx.py @@ -1,6 +1,6 @@ import requests -from testcontainers.nginx import NginxContainer +from testcontainers.community.nginx import NginxContainer def test_docker_run_nginx(): diff --git a/tests/community/ollama/test_ollama.py b/tests/community/ollama/test_ollama.py index 980dac00b..79ba1c769 100644 --- a/tests/community/ollama/test_ollama.py +++ b/tests/community/ollama/test_ollama.py @@ -3,7 +3,7 @@ from pathlib import Path import requests -from testcontainers.ollama import OllamaContainer +from testcontainers.community.ollama import OllamaContainer def random_string(length=6): diff --git a/tests/community/openfga/test_openfga.py b/tests/community/openfga/test_openfga.py index 1f1ee3dfe..3e1ec5a36 100644 --- a/tests/community/openfga/test_openfga.py +++ b/tests/community/openfga/test_openfga.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.openfga import OpenFGAContainer +from testcontainers.community.openfga import OpenFGAContainer from sys import version_info diff --git a/tests/community/opensearch/test_opensearch.py b/tests/community/opensearch/test_opensearch.py index 8a14b0b14..23a7f9828 100644 --- a/tests/community/opensearch/test_opensearch.py +++ b/tests/community/opensearch/test_opensearch.py @@ -1,4 +1,4 @@ -from testcontainers.opensearch import OpenSearchContainer +from testcontainers.community.opensearch import OpenSearchContainer import pytest diff --git a/tests/community/oracle-free/test_oracle.py b/tests/community/oracle-free/test_oracle.py index 0c6d8998e..1effbe89f 100644 --- a/tests/community/oracle-free/test_oracle.py +++ b/tests/community/oracle-free/test_oracle.py @@ -2,7 +2,7 @@ import sqlalchemy from testcontainers.core.utils import is_arm -from testcontainers.oracle import OracleDbContainer +from testcontainers.community.oracle import OracleDbContainer @pytest.mark.skipif(is_arm(), reason="oracle-free container not available for ARM") diff --git a/tests/community/postgres/test_postgres.py b/tests/community/postgres/test_postgres.py index 93b99d25f..e591e6b38 100644 --- a/tests/community/postgres/test_postgres.py +++ b/tests/community/postgres/test_postgres.py @@ -3,7 +3,7 @@ import pytest import sqlalchemy -from testcontainers.postgres import PostgresContainer +from testcontainers.community.postgres import PostgresContainer # https://www.postgresql.org/support/versioning/ diff --git a/tests/community/qdrant/test_qdrant.py b/tests/community/qdrant/test_qdrant.py index d3b59e57c..82b9aaa68 100644 --- a/tests/community/qdrant/test_qdrant.py +++ b/tests/community/qdrant/test_qdrant.py @@ -1,5 +1,5 @@ import pytest -from testcontainers.qdrant import QdrantContainer +from testcontainers.community.qdrant import QdrantContainer import uuid from grpc import RpcError from pathlib import Path diff --git a/tests/community/rabbitmq/test_rabbitmq.py b/tests/community/rabbitmq/test_rabbitmq.py index c0c1894d8..ea0051819 100644 --- a/tests/community/rabbitmq/test_rabbitmq.py +++ b/tests/community/rabbitmq/test_rabbitmq.py @@ -4,7 +4,7 @@ import pika import pytest -from testcontainers.rabbitmq import RabbitMqContainer +from testcontainers.community.rabbitmq import RabbitMqContainer QUEUE = "test-q" EXCHANGE = "test-exchange" diff --git a/tests/community/redis/test_redis.py b/tests/community/redis/test_redis.py index 01be35f14..08b89455a 100644 --- a/tests/community/redis/test_redis.py +++ b/tests/community/redis/test_redis.py @@ -1,6 +1,6 @@ import time -from testcontainers.redis import RedisContainer, AsyncRedisContainer +from testcontainers.community.redis import RedisContainer, AsyncRedisContainer import pytest import redis diff --git a/tests/community/registry/test_registry.py b/tests/community/registry/test_registry.py index f9d77d973..c7d4f6c9e 100644 --- a/tests/community/registry/test_registry.py +++ b/tests/community/registry/test_registry.py @@ -1,6 +1,6 @@ from requests import Response, get from requests.auth import HTTPBasicAuth -from testcontainers.registry import DockerRegistryContainer +from testcontainers.community.registry import DockerRegistryContainer REGISTRY_USERNAME: str = "foo" diff --git a/tests/community/scylla/test_scylla.py b/tests/community/scylla/test_scylla.py index 6ffaae2fc..5209c3359 100644 --- a/tests/community/scylla/test_scylla.py +++ b/tests/community/scylla/test_scylla.py @@ -1,4 +1,4 @@ -from testcontainers.scylla import ScyllaContainer +from testcontainers.community.scylla import ScyllaContainer def test_docker_run_scylla(): diff --git a/tests/community/selenium/test_selenium.py b/tests/community/selenium/test_selenium.py index ac243c27b..190b5d2e4 100644 --- a/tests/community/selenium/test_selenium.py +++ b/tests/community/selenium/test_selenium.py @@ -7,7 +7,7 @@ from selenium.webdriver.common.by import By from testcontainers.core.utils import is_arm -from testcontainers.selenium import BrowserWebDriverContainer +from testcontainers.community.selenium import BrowserWebDriverContainer @pytest.mark.parametrize("caps", [DesiredCapabilities.CHROME, DesiredCapabilities.FIREFOX]) diff --git a/tests/community/sftp/test_sftp.py b/tests/community/sftp/test_sftp.py index e3dab2e3b..695ace1bd 100644 --- a/tests/community/sftp/test_sftp.py +++ b/tests/community/sftp/test_sftp.py @@ -4,7 +4,7 @@ import paramiko import pytest -from testcontainers.sftp import SFTPContainer, SFTPUser +from testcontainers.community.sftp import SFTPContainer, SFTPUser def test_sftp_login_with_default_basic_auth(): diff --git a/tests/community/trino/test_trino.py b/tests/community/trino/test_trino.py index c1a70230b..5f6fc4821 100644 --- a/tests/community/trino/test_trino.py +++ b/tests/community/trino/test_trino.py @@ -1,4 +1,4 @@ -from testcontainers.trino import TrinoContainer +from testcontainers.community.trino import TrinoContainer from trino.dbapi import connect diff --git a/tests/community/valkey/test_valkey.py b/tests/community/valkey/test_valkey.py index bcdf590ed..3e5d70024 100644 --- a/tests/community/valkey/test_valkey.py +++ b/tests/community/valkey/test_valkey.py @@ -1,6 +1,6 @@ import socket -from testcontainers.valkey import ValkeyContainer +from testcontainers.community.valkey import ValkeyContainer def test_docker_run_valkey(): diff --git a/tests/community/vault/test_vault.py b/tests/community/vault/test_vault.py index 54017d2f9..4c6b6b605 100644 --- a/tests/community/vault/test_vault.py +++ b/tests/community/vault/test_vault.py @@ -1,5 +1,5 @@ import hvac -from testcontainers.vault import VaultContainer +from testcontainers.community.vault import VaultContainer def test_docker_run_vault(): diff --git a/tests/community/weaviate/test_weaviate.py b/tests/community/weaviate/test_weaviate.py index 40728d4aa..e10ac4f97 100644 --- a/tests/community/weaviate/test_weaviate.py +++ b/tests/community/weaviate/test_weaviate.py @@ -1,4 +1,4 @@ -from testcontainers.weaviate import WeaviateContainer +from testcontainers.community.weaviate import WeaviateContainer import weaviate From 85160bdaa2ee7d92ce85fb34b92cb0d22d8b6275 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 02:46:09 +0200 Subject: [PATCH 06/29] feat(test): start working on parallel-running tests (WIP!) Some related errors were fixed: - ryuk: increase container removal timeout 30s -> 60s; Docker is slower to clean up when many containers are running concurrently - socat: helloworld container had no wait strategy, causing ConnectionResetError when socat forwarded requests before the HTTP server was ready - db2/mssql: mark with xdist_group to prevent parametrized versions from running in parallel; concurrent instances caused OOM kills and resource exhaustion Co-Authored-By: Claude Sonnet 4.6 --- pyproject.toml | 13 +++---------- tests/community/db2/test_db2.py | 2 ++ tests/community/mssql/test_mssql.py | 2 ++ tests/core/test_ryuk.py | 2 +- tests/core/test_socat.py | 4 +++- uv.lock | 26 ++++++++++++++++++++++++++ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ab53ea7a1..17eb176ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -132,6 +132,7 @@ test = [ "twine>=6.2.0", "anyio>=4", "tomli", # required for pyproject.toml tests, TODO: remove once we drop py3.10 support + "pytest-xdist>=3.8.0", ] lint = [ "mypy>=1", @@ -162,11 +163,8 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build] -# If you build sdists, you probably want to include core/ + modules/ in the sdist. -# Adjust as needed. include = [ - "core/**", - "modules/**", + "src/**", "README.md", "LICENSE*", "pyproject.toml", @@ -270,12 +268,7 @@ pretty = true show_error_codes = true warn_return_any = true strict = true -modules = ["testcontainers.core"] -mypy_path = [ - "core", - "modules/mailpit", - "modules/sftp", -] +mypy_path = ["src"] enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] [[tool.mypy.overrides]] diff --git a/tests/community/db2/test_db2.py b/tests/community/db2/test_db2.py index 6b3ebba1e..1484814e9 100644 --- a/tests/community/db2/test_db2.py +++ b/tests/community/db2/test_db2.py @@ -6,6 +6,8 @@ from testcontainers.core.utils import is_arm from testcontainers.community.db2 import Db2Container +pytestmark = pytest.mark.xdist_group("db2") + @pytest.mark.skipif(is_arm(), reason="db2 container not available for ARM") @pytest.mark.parametrize("version", ["11.5.9.0", "11.5.8.0"]) diff --git a/tests/community/mssql/test_mssql.py b/tests/community/mssql/test_mssql.py index b0cb09d03..22a282ece 100644 --- a/tests/community/mssql/test_mssql.py +++ b/tests/community/mssql/test_mssql.py @@ -4,6 +4,8 @@ from testcontainers.core.utils import is_arm from testcontainers.community.mssql import SqlServerContainer +pytestmark = pytest.mark.xdist_group("mssql") + @pytest.mark.skipif(is_arm(), reason="mssql container not available for ARM") @pytest.mark.parametrize("version", ["2022-CU12-ubuntu-22.04", "2019-CU25-ubuntu-20.04"]) diff --git a/tests/core/test_ryuk.py b/tests/core/test_ryuk.py index ed3bdd7ce..18d2a1c29 100644 --- a/tests/core/test_ryuk.py +++ b/tests/core/test_ryuk.py @@ -12,7 +12,7 @@ from testcontainers.core.waiting_utils import wait_for_logs -def _wait_for_container_removed(client: DockerClient, container_id: str, timeout: float = 30) -> None: +def _wait_for_container_removed(client: DockerClient, container_id: str, timeout: float = 60) -> None: """Poll until a container is fully removed (raises NotFound).""" start = perf_counter() while perf_counter() - start < timeout: diff --git a/tests/core/test_socat.py b/tests/core/test_socat.py index 19d4fe5ce..fb7c91b64 100644 --- a/tests/core/test_socat.py +++ b/tests/core/test_socat.py @@ -2,6 +2,7 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.network import Network +from testcontainers.core.wait_strategies import HttpWaitStrategy from testcontainers.socat.socat import SocatContainer @@ -11,7 +12,8 @@ def test_socat_with_helloworld(): DockerContainer("testcontainers/helloworld:1.2.0") .with_exposed_ports(8080) .with_network(network) - .with_network_aliases("helloworld"), + .with_network_aliases("helloworld") + .waiting_for(HttpWaitStrategy(8080, "/ping")), SocatContainer().with_network(network).with_target(8080, "helloworld") as socat, ): socat_url = f"http://{socat.get_container_host_ip()}:{socat.get_exposed_port(8080)}" diff --git a/uv.lock b/uv.lock index 8fea71ec7..2bce6b6a9 100644 --- a/uv.lock +++ b/uv.lock @@ -1215,6 +1215,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +] + [[package]] name = "filelock" version = "3.29.0" @@ -4377,6 +4386,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, +] + [[package]] name = "python-arango" version = "8.3.3" @@ -5493,6 +5515,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "pytest-xdist" }, { name = "ruff" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, @@ -5534,6 +5557,7 @@ test = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "pytest-xdist" }, { name = "sqlalchemy" }, { name = "sqlalchemy-cockroachdb" }, { name = "tomli" }, @@ -5618,6 +5642,7 @@ dev = [ { name = "pytest-asyncio", specifier = ">=1" }, { name = "pytest-cov", specifier = ">=7" }, { name = "pytest-mock", specifier = ">=3" }, + { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "ruff" }, { name = "sphinx", marker = "python_full_version < '3.11'", specifier = ">=8" }, { name = "sphinx", marker = "python_full_version >= '3.11'", specifier = ">=9" }, @@ -5657,6 +5682,7 @@ test = [ { name = "pytest-asyncio", specifier = ">=1" }, { name = "pytest-cov", specifier = ">=7" }, { name = "pytest-mock", specifier = ">=3" }, + { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "sqlalchemy", specifier = ">=2" }, { name = "sqlalchemy-cockroachdb", specifier = ">=2" }, { name = "tomli" }, From f82abe8c32ff431cc9d481af3d6beb762214dca8 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:08:36 +0200 Subject: [PATCH 07/29] chore(cassandra): update tests - use new driver: no workaround required anymore - set protocol_version=5 for Cassandra 4.x to suppress negotiation warnings Co-Authored-By: Claude Sonnet 4.6 --- doctests/conf.py | 17 ----------------- pyproject.toml | 2 +- .../community/cassandra/__init__.py | 2 +- tests/community/cassandra/test_cassandra.py | 18 ++++++------------ uv.lock | 14 ++++++++------ 5 files changed, 16 insertions(+), 37 deletions(-) diff --git a/doctests/conf.py b/doctests/conf.py index ac0e3fb4c..0822df226 100644 --- a/doctests/conf.py +++ b/doctests/conf.py @@ -3,20 +3,3 @@ "sphinx.ext.doctest", ] master_doc = "README" - -doctest_global_setup = r""" -import sys -from importlib.metadata import version, PackageNotFoundError -from packaging.version import Version - -try: - _cassandra_driver_version = Version(version("cassandra-driver")) -except PackageNotFoundError: - _cassandra_driver_version = None - -SKIP_CASSANDRA_EXAMPLE = ( - _cassandra_driver_version is not None - and _cassandra_driver_version <= Version("3.29.3") - and sys.version_info > (3, 14) -) -""" diff --git a/pyproject.toml b/pyproject.toml index 17eb176ad..c554fc1fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,7 +122,7 @@ test = [ "psycopg2-binary==2.9.11", "pg8000==1.31.5", "psycopg>=3", - "cassandra-driver>=3; python_version < '3.14'", + "cassandra-driver>=3.30", "kafka-python-ng>=2", "hvac>=2; python_version < '4.0'", "pymilvus>=2", diff --git a/src/testcontainers/community/cassandra/__init__.py b/src/testcontainers/community/cassandra/__init__.py index 813be8b1d..08a801354 100644 --- a/src/testcontainers/community/cassandra/__init__.py +++ b/src/testcontainers/community/cassandra/__init__.py @@ -21,7 +21,6 @@ class CassandraContainer(DockerContainer): Example: .. doctest:: cassandra_container - :skipif: SKIP_CASSANDRA_EXAMPLE >>> from testcontainers.community.cassandra import CassandraContainer >>> from cassandra.cluster import Cluster, DCAwareRoundRobinPolicy @@ -29,6 +28,7 @@ class CassandraContainer(DockerContainer): >>> with CassandraContainer("cassandra:4.1.4") as cassandra, Cluster( ... cassandra.get_contact_points(), ... load_balancing_policy=DCAwareRoundRobinPolicy(cassandra.get_local_datacenter()), + ... protocol_version=5, ... ) as cluster: ... session = cluster.connect() ... result = session.execute("SELECT release_version FROM system.local;") diff --git a/tests/community/cassandra/test_cassandra.py b/tests/community/cassandra/test_cassandra.py index c9558997e..7acfd249d 100644 --- a/tests/community/cassandra/test_cassandra.py +++ b/tests/community/cassandra/test_cassandra.py @@ -1,23 +1,17 @@ from testcontainers.community.cassandra import CassandraContainer -import sys -from importlib.metadata import version -import pytest -from packaging.version import Version - -@pytest.mark.skipif( - Version(version("cassandra-driver")) <= Version("3.29.3") and sys.version_info > (3, 14), - reason="cassandra-driver <= 3.29.3 is incompatible with Python > 3.14", -) def test_docker_run_cassandra() -> None: from cassandra.cluster import Cluster, DCAwareRoundRobinPolicy - with CassandraContainer("cassandra:4.1.4") as cassandra: - cluster = Cluster( + with ( + CassandraContainer("cassandra:4.1.4") as cassandra, + Cluster( cassandra.get_contact_points(), load_balancing_policy=DCAwareRoundRobinPolicy(cassandra.get_local_datacenter()), - ) + protocol_version=5, + ) as cluster, + ): session = cluster.connect() result = session.execute("SELECT release_version FROM system.local;") assert result.one().release_version == "4.1.4" diff --git a/uv.lock b/uv.lock index 2bce6b6a9..95d777bea 100644 --- a/uv.lock +++ b/uv.lock @@ -550,8 +550,8 @@ name = "cassandra-driver" version = "3.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated", marker = "python_full_version < '3.14'" }, - { name = "geomet", marker = "python_full_version < '3.14'" }, + { name = "deprecated" }, + { name = "geomet" }, ] sdist = { url = "https://files.pythonhosted.org/packages/85/26/8806d0949422b560029a040e7a628d92addd612726468c3fb546354a43a4/cassandra_driver-3.30.0.tar.gz", hash = "sha256:7e4cfd6ec3023576ed0ffa34882d9778e4bacfd918048ae9139ccdd00628ed85", size = 4242039, upload-time = "2026-04-16T05:04:59.838Z" } wheels = [ @@ -1097,7 +1097,7 @@ name = "deprecated" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "wrapt", marker = "python_full_version < '3.14'" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ @@ -1359,7 +1359,7 @@ name = "geomet" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "python_full_version < '3.14'" }, + { name = "click" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2a/8c/dde022aa6747b114f6b14a7392871275dea8867e2bd26cddb80cc6d66620/geomet-1.1.0.tar.gz", hash = "sha256:51e92231a0ef6aaa63ac20c443377ba78a303fd2ecd179dc3567de79f3c11605", size = 28732, upload-time = "2023-11-14T15:43:36.764Z" } wheels = [ @@ -5495,6 +5495,7 @@ weaviate = [ [package.dev-dependencies] dev = [ { name = "anyio" }, + { name = "boto3-stubs-lite", extra = ["boto3"] }, { name = "cassandra-driver", marker = "python_full_version < '3.14'" }, { name = "hvac", marker = "python_full_version < '4'" }, { name = "kafka-python-ng" }, @@ -5544,7 +5545,7 @@ lint = [ ] test = [ { name = "anyio" }, - { name = "cassandra-driver", marker = "python_full_version < '3.14'" }, + { name = "cassandra-driver" }, { name = "hvac", marker = "python_full_version < '4'" }, { name = "kafka-python-ng" }, { name = "paho-mqtt" }, @@ -5622,6 +5623,7 @@ provides-extras = ["arangodb", "aws", "azurite", "cassandra", "clickhouse", "cos [package.metadata.requires-dev] dev = [ { name = "anyio", specifier = ">=4" }, + { name = "boto3-stubs-lite", extras = ["boto3"] }, { name = "cassandra-driver", marker = "python_full_version < '3.14'", specifier = ">=3" }, { name = "hvac", marker = "python_full_version < '4'", specifier = ">=2" }, { name = "kafka-python-ng", specifier = ">=2" }, @@ -5669,7 +5671,7 @@ lint = [ ] test = [ { name = "anyio", specifier = ">=4" }, - { name = "cassandra-driver", marker = "python_full_version < '3.14'", specifier = ">=3" }, + { name = "cassandra-driver", specifier = ">=3.30" }, { name = "hvac", marker = "python_full_version < '4'", specifier = ">=2" }, { name = "kafka-python-ng", specifier = ">=2" }, { name = "paho-mqtt", specifier = ">=2" }, From e237fac7d5a6d124850ca87ee888a75914a493ac Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:38:10 +0200 Subject: [PATCH 08/29] refactor(qdrant): convert to waiting_for() wait strategy Co-Authored-By: Claude Sonnet 4.6 --- src/testcontainers/community/qdrant/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/testcontainers/community/qdrant/__init__.py b/src/testcontainers/community/qdrant/__init__.py index 828289910..9341246cc 100644 --- a/src/testcontainers/community/qdrant/__init__.py +++ b/src/testcontainers/community/qdrant/__init__.py @@ -54,13 +54,11 @@ def __init__( self.with_volume_mapping(host=str(config_file_path), container=QdrantContainer.QDRANT_CONFIG_FILE_PATH) self.with_exposed_ports(self._rest_port, self._grpc_port) + self.waiting_for(LogMessageWaitStrategy(".*Actix runtime found; starting in Actix runtime.*")) def _configure(self) -> None: self.with_env("QDRANT__SERVICE__API_KEY", self._api_key) - def _connect(self) -> None: - LogMessageWaitStrategy(".*Actix runtime found; starting in Actix runtime.*").wait_until_ready(self) - def get_client(self, **kwargs): """ Get a `qdrant_client.QdrantClient` instance associated with the container. From 5f52944951a573d407433c81aa25fae55e74cd6d Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:38:49 +0200 Subject: [PATCH 09/29] fix(weaviate): update and fix module - Version bump 1.24.5 -> 1.28.4 - Add dedicated RFC1918 network (10.10.10.0/24) so Weaviate's memberlist gossip always advertises a private IP - Switch container config from CLI args to env vars - Replace _connect() + wait_container_is_ready with HttpWaitStrategy - Use DockerContainer as base class instead of DbContainer - fix(image): assert _image not None in get_wrapped_image() Co-Authored-By: Claude Sonnet 4.6 --- .../community/weaviate/__init__.py | 48 ++++++++++++------- src/testcontainers/core/image.py | 9 +++- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/testcontainers/community/weaviate/__init__.py b/src/testcontainers/community/weaviate/__init__.py index 6049aaee6..c8c95bb3a 100644 --- a/src/testcontainers/community/weaviate/__init__.py +++ b/src/testcontainers/community/weaviate/__init__.py @@ -10,17 +10,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from typing import TYPE_CHECKING, Optional +import contextlib +from typing import Optional -from requests import ConnectionError, get -from testcontainers.core.generic import DbContainer -from testcontainers.core.waiting_utils import wait_container_is_ready +from docker.types import IPAMConfig, IPAMPool +from testcontainers.core.container import DockerContainer +from testcontainers.core.network import Network +from testcontainers.core.wait_strategies import HttpWaitStrategy +from typing_extensions import Self -if TYPE_CHECKING: - from requests import Response - -class WeaviateContainer(DbContainer): +class WeaviateContainer(DockerContainer): """ Weaviate vector database container. @@ -62,7 +62,7 @@ class WeaviateContainer(DbContainer): def __init__( self, - image: str = "semitechnologies/weaviate:1.24.5", + image: str = "semitechnologies/weaviate:1.28.4", env_vars: Optional[dict[str, str]] = None, **kwargs, ) -> None: @@ -70,22 +70,38 @@ def __init__( self._http_port = 8080 self._grpc_port = 50051 - self.with_command(f"--host 0.0.0.0 --scheme http --port {self._http_port}") + # Weaviate's memberlist gossip requires an RFC1918 private IP to advertise. + # Docker bridge subnets are not always in RFC1918 ranges, so we create a + # dedicated network using 10.10.10.0/24 which is always recognised as private. + self._weaviate_network = Network( + docker_network_kw={"ipam": IPAMConfig(pool_configs=[IPAMPool(subnet="10.10.10.0/24")])} + ) + self.with_network(self._weaviate_network) self.with_exposed_ports(self._http_port, self._grpc_port) + self.waiting_for(HttpWaitStrategy(self._http_port, "/v1/.well-known/ready")) if env_vars is not None: for key, value in env_vars.items(): self.with_env(key, value) def _configure(self) -> None: + self.with_env("HOST", "0.0.0.0") + self.with_env("PORT", str(self._http_port)) + self.with_env("SCHEME", "http") self.with_env("AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", "true") self.with_env("PERSISTENCE_DATA_PATH", "/var/lib/weaviate") - - @wait_container_is_ready(ConnectionError) - def _connect(self) -> None: - url = f"http://{self.get_http_host()}:{self.get_http_port()}/v1/.well-known/ready" - response: Response = get(url) - response.raise_for_status() + self.with_env("DEFAULT_VECTORIZER_MODULE", "none") + self.with_env("CLUSTER_HOSTNAME", "node1") + + def start(self) -> Self: + self._weaviate_network.create() + return super().start() + + def stop(self, force: bool = True, delete_volume: bool = True) -> None: + super().stop(force=force, delete_volume=delete_volume) + if self._weaviate_network._network is not None: + with contextlib.suppress(Exception): + self._weaviate_network.remove() def get_client( self, diff --git a/src/testcontainers/core/image.py b/src/testcontainers/core/image.py index eedb2ce40..f4e6132d5 100644 --- a/src/testcontainers/core/image.py +++ b/src/testcontainers/core/image.py @@ -2,6 +2,7 @@ from types import TracebackType from typing import TYPE_CHECKING, Any, Optional, Union +import docker.errors from typing_extensions import Self from testcontainers.core.docker_client import DockerClient @@ -85,8 +86,11 @@ def remove(self, force: bool = True, noprune: bool = False) -> None: :param noprune: Do not delete untagged parent images """ if self._image and self.clean_up: - logger.info(f"Removing image {self.short_id}") - self._image.remove(force=force, noprune=noprune) + try: + logger.info(f"Removing image {self.short_id}") + self._image.remove(force=force, noprune=noprune) + except docker.errors.NotFound: + logger.debug(f"Image {self.short_id} already removed") self.get_docker_client().client.close() def __str__(self) -> str: @@ -101,6 +105,7 @@ def __exit__( self.remove() def get_wrapped_image(self) -> "Image": + assert self._image is not None return self._image def get_docker_client(self) -> DockerClient: From 8fe07699b478b5b5a35c8795e99f60e870879643 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:50:39 +0200 Subject: [PATCH 10/29] refactor(milvus): convert to waiting_for() wait strategy Replace manual _connect()/_healthcheck() with CompositeWaitStrategy. Also fixes a bug where _healthcheck() was called twice in start() (once inside _connect() and once directly). Move command into __init__ to remove the start() override entirely. Co-Authored-By: Claude Sonnet 4.6 --- .../community/milvus/__init__.py | 46 +++++-------------- tests/community/milvus/test_milvus.py | 2 +- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/testcontainers/community/milvus/__init__.py b/src/testcontainers/community/milvus/__init__.py index b75b12d0c..62352356d 100644 --- a/src/testcontainers/community/milvus/__init__.py +++ b/src/testcontainers/community/milvus/__init__.py @@ -11,10 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. -import requests -from testcontainers.core.config import testcontainers_config as c from testcontainers.core.generic import DockerContainer -from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs +from testcontainers.core.wait_strategies import CompositeWaitStrategy, HttpWaitStrategy, LogMessageWaitStrategy class MilvusContainer(DockerContainer): @@ -31,7 +29,7 @@ class MilvusContainer(DockerContainer): >>> from testcontainers.community.milvus import MilvusContainer >>> with MilvusContainer("milvusdb/milvus:v2.4.4") as milvus_container: - ... milvus_container.get_exposed_port(milvus_container.port) in milvus_container.get_connection_url() + ... str(milvus_container.get_exposed_port(milvus_container.port)) in milvus_container.get_connection_url() True """ @@ -45,40 +43,20 @@ def __init__( self.port = port self.healthcheck_port = 9091 self.with_exposed_ports(self.port, self.healthcheck_port) - self.cmd = "milvus run standalone" + self.with_command("milvus run standalone") - envs = {"ETCD_USE_EMBED": "true", "ETCD_DATA_DIR": "/var/lib/milvus/etcd", "COMMON_STORAGETYPE": "local"} + self.with_env("ETCD_USE_EMBED", "true") + self.with_env("ETCD_DATA_DIR", "/var/lib/milvus/etcd") + self.with_env("COMMON_STORAGETYPE", "local") - for env, value in envs.items(): - self.with_env(env, value) + self.waiting_for( + CompositeWaitStrategy( + LogMessageWaitStrategy("Welcome to use Milvus!"), + HttpWaitStrategy(self.healthcheck_port, "/healthz"), + ) + ) def get_connection_url(self) -> str: ip = self.get_container_host_ip() port = self.get_exposed_port(self.port) return f"http://{ip}:{port}" - - @wait_container_is_ready() - def _connect(self) -> None: - msg = "Welcome to use Milvus!" - wait_for_logs(self, f".*{msg}.*", c.max_tries, c.sleep_time) - self._healthcheck() - - def _get_healthcheck_url(self) -> str: - ip = self.get_container_host_ip() - port = self.get_exposed_port(self.healthcheck_port) - return f"http://{ip}:{port}" - - @wait_container_is_ready(requests.exceptions.HTTPError, requests.exceptions.ConnectionError) - def _healthcheck(self) -> None: - healthcheck_url = self._get_healthcheck_url() - response = requests.get(f"{healthcheck_url}/healthz", timeout=1) - response.raise_for_status() - - def start(self) -> "MilvusContainer": - """This method starts the Milvus container and runs the healthcheck - to verify that the container is ready to use.""" - self.with_command(self.cmd) - super().start() - self._connect() - self._healthcheck() - return self diff --git a/tests/community/milvus/test_milvus.py b/tests/community/milvus/test_milvus.py index 7f05f95a1..495f49535 100644 --- a/tests/community/milvus/test_milvus.py +++ b/tests/community/milvus/test_milvus.py @@ -21,7 +21,7 @@ def test_run_milvus_success(version: str): exposed_port = milvus_container.get_exposed_port(milvus_container.port) url = milvus_container.get_connection_url() - assert url and exposed_port in url + assert url and str(exposed_port) in url @pytest.mark.parametrize("version", VERSIONS) From 3fc8dce46482e6c4d91619979b46054d10b971a7 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:50 +0200 Subject: [PATCH 11/29] fix(trino): increase default startup timeout from 30s to 60s 30s was not enough for the JVM to emit the startup log message under load when running the full test suite in parallel. Co-Authored-By: Claude Sonnet 4.6 --- docs/modules/trino_example.py | 3 +-- src/testcontainers/community/trino/__init__.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/modules/trino_example.py b/docs/modules/trino_example.py index b2466ca23..3174a8260 100644 --- a/docs/modules/trino_example.py +++ b/docs/modules/trino_example.py @@ -1,7 +1,6 @@ import trino -from trino.exceptions import TrinoQueryError - from testcontainers.community.trino import TrinoContainer +from trino.exceptions import TrinoQueryError def basic_example(): diff --git a/src/testcontainers/community/trino/__init__.py b/src/testcontainers/community/trino/__init__.py index 4532260b2..4e2b1f83c 100644 --- a/src/testcontainers/community/trino/__init__.py +++ b/src/testcontainers/community/trino/__init__.py @@ -23,7 +23,7 @@ def __init__( image="trinodb/trino:latest", user: str = "test", port: int = 8080, - container_start_timeout: int = 30, + container_start_timeout: int = 60, wait_strategy_check_string: str = ".*======== SERVER STARTED ========.*", **kwargs, ): From 97ba28e7e77fca148f449342d55986ee68ec9097 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:51 +0200 Subject: [PATCH 12/29] fix(keycloak): disable SSL requirement for master realm after start Keycloak 26+ enforces SSL by default even for localhost, causing client connections over plain HTTP to fail. Disable it via kcadm.sh post-start. Also add user_realm_name to get_client() kwargs to fix authentication against the master realm. Co-Authored-By: Claude Sonnet 4.6 --- .../community/keycloak/__init__.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/testcontainers/community/keycloak/__init__.py b/src/testcontainers/community/keycloak/__init__.py index 459e4ea26..ef79f31a4 100644 --- a/src/testcontainers/community/keycloak/__init__.py +++ b/src/testcontainers/community/keycloak/__init__.py @@ -112,8 +112,31 @@ def _readiness_probe(self) -> None: def start(self) -> "KeycloakContainer": super().start() self._readiness_probe() + self._disable_ssl_required() return self + def _disable_ssl_required(self) -> None: + result = self.get_wrapped_container().exec_run( + [ + "/opt/keycloak/bin/kcadm.sh", + "update", + "realms/master", + "-s", + "sslRequired=none", + "--no-config", + "--server", + f"http://localhost:{self.port}", + "--realm", + "master", + "--user", + self.username, + "--password", + self.password, + ] + ) + if result.exit_code != 0: + raise RuntimeError(f"Failed to disable SSL for master realm: {result.output.decode()}") + def with_realm_import_file(self, realm_import_file: str) -> "KeycloakContainer": file = os.path.abspath(realm_import_file) if not os.path.exists(file): @@ -136,6 +159,7 @@ def get_client(self, **kwargs) -> KeycloakAdmin: "username": self.username, "password": self.password, "realm_name": "master", + "user_realm_name": "master", "verify": True, } kwargs = {**default_kwargs, **kwargs} From e602d749b01023aeadac13b11d1c26fb60c67f30 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:51 +0200 Subject: [PATCH 13/29] fix(k3s): replace tmpfs dict kwarg with with_tmpfs_mount() Passing tmpfs as a Docker kwarg dict is no longer supported in the new container API. Use the dedicated with_tmpfs_mount() method instead. Co-Authored-By: Claude Sonnet 4.6 --- src/testcontainers/community/k3s/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testcontainers/community/k3s/__init__.py b/src/testcontainers/community/k3s/__init__.py index 8d1216fc4..02a3be693 100644 --- a/src/testcontainers/community/k3s/__init__.py +++ b/src/testcontainers/community/k3s/__init__.py @@ -44,7 +44,9 @@ def __init__(self, image="rancher/k3s:latest", enable_cgroup_mount=True, **kwarg self.with_exposed_ports(self.KUBE_SECURE_PORT, self.RANCHER_WEBHOOK_PORT) self.with_env("K3S_URL", f"https://{self.get_container_host_ip()}:{self.KUBE_SECURE_PORT}") self.with_command("server --disable traefik --tls-san=" + self.get_container_host_ip()) - self.with_kwargs(privileged=True, tmpfs={"/run": "", "/var/run": ""}) + self.with_kwargs(privileged=True) + self.with_tmpfs_mount("/run") + self.with_tmpfs_mount("/var/run") if enable_cgroup_mount: self.with_volume_mapping("/sys/fs/cgroup", "/sys/fs/cgroup", "rw") else: From 1e6f681d0877e4c5b87b4e3192f8a1678db6cf55 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:52 +0200 Subject: [PATCH 14/29] fix(sftp): remove redundant start() calls and fix SSH connection options Calling sftp_container.start() inside a with block double-starts the container (the context manager already calls start()). Also add allow_agent=False and look_for_keys=False to paramiko connect() calls to prevent the SSH client from picking up ambient keys/agents from the test environment, which caused intermittent auth failures. Co-Authored-By: Claude Sonnet 4.6 --- tests/community/sftp/test_sftp.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/community/sftp/test_sftp.py b/tests/community/sftp/test_sftp.py index 695ace1bd..f9e46c442 100644 --- a/tests/community/sftp/test_sftp.py +++ b/tests/community/sftp/test_sftp.py @@ -9,7 +9,6 @@ def test_sftp_login_with_default_basic_auth(): with SFTPContainer() as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -19,12 +18,13 @@ def test_sftp_login_with_default_basic_auth(): port=host_port, username=sftp_container.users[0].name, password=sftp_container.users[0].password, + allow_agent=False, + look_for_keys=False, ) def test_sftp_login_with_default_keypair_auth(): with SFTPContainer() as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -40,7 +40,6 @@ def test_sftp_login_with_default_keypair_auth(): def test_sftp_login_with_custom_user_basic_auth(): user = SFTPUser(name="custom", password="custom_password") with SFTPContainer(users=[user]) as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -50,13 +49,14 @@ def test_sftp_login_with_custom_user_basic_auth(): port=host_port, username=user.name, password=user.password, + allow_agent=False, + look_for_keys=False, ) def test_sftp_login_with_custom_user_keypair_auth(): user = SFTPUser.with_keypair(name="custom") with SFTPContainer(users=[user]) as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -72,7 +72,6 @@ def test_sftp_login_with_custom_user_keypair_auth(): def test_sftp_login_with_custom_user_password_and_keypair_auth(): user = SFTPUser.with_keypair(name="custom", password="custom_password") with SFTPContainer(users=[user]) as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -88,7 +87,6 @@ def test_sftp_login_with_custom_user_password_and_keypair_auth(): def test_sftp_user_can_upload(): with SFTPContainer() as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -98,6 +96,8 @@ def test_sftp_user_can_upload(): port=host_port, username=sftp_container.users[0].name, password=sftp_container.users[0].password, + allow_agent=False, + look_for_keys=False, ) sftp = ssh.open_sftp() sftp.chdir("upload") @@ -119,7 +119,6 @@ def test_sftp_user_can_download_from_mounted(tmp_path: Path): temp_file.write_text("test") user = SFTPUser.with_keypair(name="custom", mount_dir=temp_dir.as_posix()) with SFTPContainer(users=[user]) as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -143,7 +142,6 @@ def test_sftp_user_cant_upload_to_root(tmp_path: Path): temp_file = temp_dir / "test.txt" temp_file.write_text("test") with SFTPContainer() as sftp_container: - sftp_container.start() host_ip = sftp_container.get_container_host_ip() host_port = sftp_container.get_exposed_sftp_port() ssh = paramiko.SSHClient() @@ -153,6 +151,8 @@ def test_sftp_user_cant_upload_to_root(tmp_path: Path): port=host_port, username=sftp_container.users[0].name, password=sftp_container.users[0].password, + allow_agent=False, + look_for_keys=False, ) sftp = ssh.open_sftp() with pytest.raises(PermissionError): From 42d9fbf08a8a76726df03820fbfaed19c59cc175 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:52 +0200 Subject: [PATCH 15/29] fix(arangodb): replace deprecated add_hash_index with add_persistent_index add_hash_index() was removed in python-arango >= 8; persistent indexes are the recommended replacement and cover the same use case. Co-Authored-By: Claude Sonnet 4.6 --- tests/community/arangodb/test_arangodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/community/arangodb/test_arangodb.py b/tests/community/arangodb/test_arangodb.py index 1a8df606e..00ac2b927 100644 --- a/tests/community/arangodb/test_arangodb.py +++ b/tests/community/arangodb/test_arangodb.py @@ -34,7 +34,7 @@ def arango_test_ops(arango_client, expected_version, username="root", password=" students = database.create_collection("students") # Add a hash index to the collection. - students.add_hash_index(fields=["name"], unique=True) + students.add_persistent_index(fields=["name"], unique=True) # Insert new documents into the collection. (students_to_insert_cnt) students.insert({"name": "jane", "age": 39}) From 7bad908e7362f1b9b99df93170dfe9d22695e669 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:53 +0200 Subject: [PATCH 16/29] fix(rabbitmq): declare queue as durable Non-durable queues are dropped on broker restart. Declaring the test queue as durable matches real-world usage and prevents flaky failures when the broker isn't fully settled. Co-Authored-By: Claude Sonnet 4.6 --- tests/community/rabbitmq/test_rabbitmq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/community/rabbitmq/test_rabbitmq.py b/tests/community/rabbitmq/test_rabbitmq.py index ea0051819..71366ce35 100644 --- a/tests/community/rabbitmq/test_rabbitmq.py +++ b/tests/community/rabbitmq/test_rabbitmq.py @@ -44,7 +44,7 @@ def test_docker_run_rabbitmq( # create exchange and queue: channel = connection.channel() channel.exchange_declare(exchange=EXCHANGE, exchange_type="topic") - channel.queue_declare(QUEUE, arguments={}) + channel.queue_declare(QUEUE, durable=True, arguments={}) channel.queue_bind(QUEUE, EXCHANGE, ROUTING_KEY) # publish message: From 0ceaf48edba215465cfa12a1dd325f91aaa72efe Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:53 +0200 Subject: [PATCH 17/29] fix(core/registry): use relative import for _LocalRegistryContainer pytest --import-mode=importlib requires explicit relative imports for local helper modules; the bare import worked by accident under the old import mode. Co-Authored-By: Claude Sonnet 4.6 --- tests/core/test_core_registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_core_registry.py b/tests/core/test_core_registry.py index 7be489a64..a6c801b2b 100644 --- a/tests/core/test_core_registry.py +++ b/tests/core/test_core_registry.py @@ -12,7 +12,7 @@ import json import pytest -from _local_registry_container import _LocalRegistryContainer # type: ignore[import-not-found] +from ._local_registry_container import _LocalRegistryContainer from docker.errors import NotFound from testcontainers.core.config import testcontainers_config From e0ed1dba6c0e0b07912399619c2fdcb73a5b4fc8 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 23:26:35 +0200 Subject: [PATCH 18/29] refactor(socat): convert to waiting_for() wait strategy Replace manual _connect() + @wait_container_is_ready(OSError) with PortWaitStrategy. Since targets are registered after __init__ via with_target(), the strategy is applied in start() just before delegating to super(). Co-Authored-By: Claude Sonnet 4.6 --- src/testcontainers/socat/socat.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/testcontainers/socat/socat.py b/src/testcontainers/socat/socat.py index bf6307e95..f53b412ad 100644 --- a/src/testcontainers/socat/socat.py +++ b/src/testcontainers/socat/socat.py @@ -11,12 +11,13 @@ # License for the specific language governing permissions and limitations # under the License. import random -import socket import string from typing import Any, Optional +from typing_extensions import Self + from testcontainers.core.container import DockerContainer -from testcontainers.core.waiting_utils import wait_container_is_ready +from testcontainers.core.wait_strategies import PortWaitStrategy class SocatContainer(DockerContainer): @@ -77,15 +78,7 @@ def _configure(self) -> None: self.with_command(f'-c "{command}"') - def start(self) -> "SocatContainer": - super().start() - self._connect() - return self - - @wait_container_is_ready(OSError) - def _connect(self) -> None: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - next_port = next(iter(self.ports)) - # todo remove this limitation - assert isinstance(next_port, int) - s.connect((self.get_container_host_ip(), int(self.get_exposed_port(next_port)))) + def start(self) -> Self: + if self.targets: + self.waiting_for(PortWaitStrategy(next(iter(self.targets)))) + return super().start() From f1f05c64a2048ad22b78527380067d319d6fde60 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 02:50:12 +0200 Subject: [PATCH 19/29] feat(main): enable typing for complete package - fix all typing errors and asserts in tests - improved type hints for main methods like docker.run. --- pyproject.toml | 8 +- .../{azurite/py.typed => __init__.py} | 0 .../community/aws/aws_lambda.py | 1 + .../community/azurite/__init__.py | 12 +- .../community/chroma/__init__.py | 1 + .../community/cosmosdb/_emulator.py | 2 +- .../community/cosmosdb/mongodb.py | 2 +- .../community/cosmosdb/nosql.py | 3 +- .../community/elasticsearch/__init__.py | 2 +- .../providers/sql_connection_wait_strategy.py | 2 +- .../community/generic/server.py | 1 + src/testcontainers/community/generic/sql.py | 2 +- .../community/google/datastore.py | 6 +- src/testcontainers/community/google/pubsub.py | 11 +- .../community/influxdb/__init__.py | 2 +- .../community/influxdb1/__init__.py | 3 +- .../community/influxdb2/__init__.py | 3 +- src/testcontainers/community/k3s/__init__.py | 2 +- .../community/kafka/__init__.py | 3 +- .../community/keycloak/__init__.py | 6 +- .../community/localstack/__init__.py | 3 +- .../community/mailpit/__init__.py | 1 + .../community/memcached/__init__.py | 2 +- .../community/minio/__init__.py | 6 +- .../community/mongodb/__init__.py | 1 + src/testcontainers/community/mqtt/__init__.py | 5 +- .../community/neo4j/__init__.py | 6 +- .../community/nginx/__init__.py | 2 +- .../community/ollama/__init__.py | 3 +- .../community/openfga/__init__.py | 1 + .../community/opensearch/__init__.py | 5 +- .../community/postgres/py.typed | 0 .../community/qdrant/__init__.py | 4 +- .../community/rabbitmq/__init__.py | 1 + .../community/redis/__init__.py | 16 +- .../community/scylla/__init__.py | 4 +- .../community/selenium/__init__.py | 8 +- .../community/selenium/video.py | 2 +- src/testcontainers/community/sftp/__init__.py | 1 + src/testcontainers/community/sftp/py.typed | 0 .../community/trino/__init__.py | 2 +- .../community/valkey/__init__.py | 2 +- .../community/weaviate/__init__.py | 3 +- src/testcontainers/core/config.py | 3 +- src/testcontainers/core/container.py | 70 +++-- src/testcontainers/core/docker_client.py | 256 ++++++++++++++---- src/testcontainers/core/image.py | 13 +- src/testcontainers/core/network.py | 2 +- src/testcontainers/core/wait_strategies.py | 4 +- src/testcontainers/core/waiting_utils.py | 2 +- .../{community/mailpit => }/py.typed | 0 tests/community/generic/test_server.py | 2 +- tests/core/test_container.py | 2 +- tests/core/test_core.py | 4 +- tests/core/test_core_ports.py | 53 ++-- tests/core/test_docker_client.py | 8 +- tests/core/test_docker_in_docker.py | 9 +- tests/core/test_image.py | 11 +- tests/core/test_network.py | 9 +- tests/core/test_utils.py | 2 +- uv.lock | 85 +++++- 61 files changed, 495 insertions(+), 190 deletions(-) rename src/testcontainers/community/{azurite/py.typed => __init__.py} (100%) delete mode 100644 src/testcontainers/community/postgres/py.typed delete mode 100644 src/testcontainers/community/sftp/py.typed rename src/testcontainers/{community/mailpit => }/py.typed (100%) diff --git a/pyproject.toml b/pyproject.toml index c554fc1fb..87e9ee298 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,6 +139,8 @@ lint = [ "types-paramiko>=4", "ruff", "pre-commit>=4", + "types-docker>=7.1.0.20260518", + "boto3-stubs-lite[boto3]", ] docs = [ "sphinx>=8; python_version < '3.11'", @@ -277,15 +279,15 @@ check_untyped_defs = true disable_error_code = ["no-untyped-def"] [[tool.mypy.overrides]] -module = ["docker.*"] +module = ["pika.*"] ignore_missing_imports = true [[tool.mypy.overrides]] -module = ["wrapt.*"] +module = ["influxdb.*"] ignore_missing_imports = true [[tool.mypy.overrides]] -module = ["requests.*"] +module = ["cassandra.*"] ignore_missing_imports = true [[tool.mypy.overrides]] diff --git a/src/testcontainers/community/azurite/py.typed b/src/testcontainers/community/__init__.py similarity index 100% rename from src/testcontainers/community/azurite/py.typed rename to src/testcontainers/community/__init__.py diff --git a/src/testcontainers/community/aws/aws_lambda.py b/src/testcontainers/community/aws/aws_lambda.py index 31565206b..3b5a71b94 100644 --- a/src/testcontainers/community/aws/aws_lambda.py +++ b/src/testcontainers/community/aws/aws_lambda.py @@ -2,6 +2,7 @@ from typing import Union import httpx + from testcontainers.community.generic.server import ServerContainer from testcontainers.core.image import DockerImage diff --git a/src/testcontainers/community/azurite/__init__.py b/src/testcontainers/community/azurite/__init__.py index 0cc400200..be381f041 100644 --- a/src/testcontainers/community/azurite/__init__.py +++ b/src/testcontainers/community/azurite/__init__.py @@ -131,17 +131,17 @@ def __get_local_connection_string(self) -> str: f"DefaultEndpointsProtocol=http;AccountName={self.account_name};AccountKey={self.account_key};" ) - if self.blob_service_port in self.ports: + if str(self.blob_service_port) in self.ports: connection_string += ( f"BlobEndpoint=http://{host_ip}:{self.get_exposed_port(self.blob_service_port)}/{self.account_name};" ) - if self.queue_service_port in self.ports: + if str(self.queue_service_port) in self.ports: connection_string += ( f"QueueEndpoint=http://{host_ip}:{self.get_exposed_port(self.queue_service_port)}/{self.account_name};" ) - if self.table_service_port in self.ports: + if str(self.table_service_port) in self.ports: connection_string += ( f"TableEndpoint=http://{host_ip}:{self.get_exposed_port(self.table_service_port)}/{self.account_name};" ) @@ -206,13 +206,13 @@ def __get_external_connection_string(self) -> str: f"DefaultEndpointsProtocol=http;AccountName={self.account_name};AccountKey={self.account_key};" ) - if self.blob_service_port in self.ports: + if str(self.blob_service_port) in self.ports: connection_string += f"BlobEndpoint=http://{host_ip}:{blob_port}/{self.account_name};" - if self.queue_service_port in self.ports: + if str(self.queue_service_port) in self.ports: connection_string += f"QueueEndpoint=http://{host_ip}:{queue_port}/{self.account_name};" - if self.table_service_port in self.ports: + if str(self.table_service_port) in self.ports: connection_string += f"TableEndpoint=http://{host_ip}:{table_port}/{self.account_name};" return connection_string diff --git a/src/testcontainers/community/chroma/__init__.py b/src/testcontainers/community/chroma/__init__.py index aa6c0b3fc..26766e4d4 100644 --- a/src/testcontainers/community/chroma/__init__.py +++ b/src/testcontainers/community/chroma/__init__.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from requests import ConnectionError, get + from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import wait_container_is_ready diff --git a/src/testcontainers/community/cosmosdb/_emulator.py b/src/testcontainers/community/cosmosdb/_emulator.py index 6db14174b..ffc79cf73 100644 --- a/src/testcontainers/community/cosmosdb/_emulator.py +++ b/src/testcontainers/community/cosmosdb/_emulator.py @@ -40,7 +40,7 @@ def __init__( bind_ports: bool = os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true").strip().lower() in _ENV_TRUTHY, endpoint_ports: Iterable[int] = [], **other_kwargs, - ): + ) -> None: super().__init__(image=image, **other_kwargs) self.endpoint_ports = endpoint_ports self.partition_count = partition_count diff --git a/src/testcontainers/community/cosmosdb/mongodb.py b/src/testcontainers/community/cosmosdb/mongodb.py index e31b917b9..c46d472d5 100644 --- a/src/testcontainers/community/cosmosdb/mongodb.py +++ b/src/testcontainers/community/cosmosdb/mongodb.py @@ -30,7 +30,7 @@ def __init__( "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest" ), **other_kwargs, - ): + ) -> None: super().__init__(image=image, endpoint_ports=[ENDPOINT_PORT], **other_kwargs) assert mongodb_version is not None, "A MongoDB version is required to use the MongoDB Endpoint" self.mongodb_version = mongodb_version diff --git a/src/testcontainers/community/cosmosdb/nosql.py b/src/testcontainers/community/cosmosdb/nosql.py index 58db8525b..864dccbfd 100644 --- a/src/testcontainers/community/cosmosdb/nosql.py +++ b/src/testcontainers/community/cosmosdb/nosql.py @@ -1,6 +1,7 @@ from azure.core.exceptions import ServiceRequestError from azure.cosmos import CosmosClient as SyncCosmosClient from azure.cosmos.aio import CosmosClient as AsyncCosmosClient + from testcontainers.core.waiting_utils import wait_container_is_ready from ._emulator import CosmosDBEmulatorContainer @@ -33,7 +34,7 @@ class CosmosDBNoSQLEndpointContainer(CosmosDBEmulatorContainer): """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: object) -> None: super().__init__(endpoint_ports=[NOSQL_PORT], **kwargs) @property diff --git a/src/testcontainers/community/elasticsearch/__init__.py b/src/testcontainers/community/elasticsearch/__init__.py index ebda06b17..816e441cd 100644 --- a/src/testcontainers/community/elasticsearch/__init__.py +++ b/src/testcontainers/community/elasticsearch/__init__.py @@ -72,7 +72,7 @@ class ElasticSearchContainer(DockerContainer): '8.3.3' """ - def __init__(self, image: str = "elasticsearch", port: int = 9200, **kwargs) -> None: + def __init__(self, image: str = "elasticsearch", port: int = 9200, **kwargs: object) -> None: raise_for_deprecated_parameter(kwargs, "port_to_expose", "port") super().__init__(image, _wait_strategy=HttpWaitStrategy(port), **kwargs) self.port = port diff --git a/src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py b/src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py index bad46c743..a1e1c64f2 100644 --- a/src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py +++ b/src/testcontainers/community/generic/providers/sql_connection_wait_strategy.py @@ -19,7 +19,7 @@ class SqlAlchemyConnectWaitStrategy(WaitStrategy): """Wait strategy for database connectivity testing using SQLAlchemy.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.with_transient_exceptions(TimeoutError, ConnectionError, *ADDITIONAL_TRANSIENT_ERRORS) diff --git a/src/testcontainers/community/generic/server.py b/src/testcontainers/community/generic/server.py index 1d54fd0fc..3c6c6ccfe 100644 --- a/src/testcontainers/community/generic/server.py +++ b/src/testcontainers/community/generic/server.py @@ -1,6 +1,7 @@ from typing import Union import httpx + from testcontainers.core.container import DockerContainer from testcontainers.core.exceptions import ContainerStartException from testcontainers.core.image import DockerImage diff --git a/src/testcontainers/community/generic/sql.py b/src/testcontainers/community/generic/sql.py index c7ed755ed..c5e389768 100644 --- a/src/testcontainers/community/generic/sql.py +++ b/src/testcontainers/community/generic/sql.py @@ -20,7 +20,7 @@ class SqlContainer(DockerContainer): Note: `SqlAlchemyConnectWaitStrategy` from `sql_connection_wait_strategy` is a provided wait strategy for SQL databases. """ - def __init__(self, image: str, wait_strategy: WaitStrategy, **kwargs): + def __init__(self, image: str, wait_strategy: WaitStrategy, **kwargs: object) -> None: """ Initialize SqlContainer with optional wait strategy. diff --git a/src/testcontainers/community/google/datastore.py b/src/testcontainers/community/google/datastore.py index f97b38e8b..ff0540999 100644 --- a/src/testcontainers/community/google/datastore.py +++ b/src/testcontainers/community/google/datastore.py @@ -13,11 +13,11 @@ import os from unittest.mock import patch +from google.cloud import datastore + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs -from google.cloud import datastore - class DatastoreContainer(DockerContainer): """ @@ -56,7 +56,7 @@ def __init__( def get_datastore_emulator_host(self) -> str: return f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port)}" - def get_datastore_client(self, **kwargs) -> datastore.Client: + def get_datastore_client(self, **kwargs: object) -> datastore.Client: wait_for_logs(self, "Dev App Server is now running.", timeout=30.0) env_vars = { "DATASTORE_DATASET": self.project, diff --git a/src/testcontainers/community/google/pubsub.py b/src/testcontainers/community/google/pubsub.py index b9ea4151c..b701c761d 100644 --- a/src/testcontainers/community/google/pubsub.py +++ b/src/testcontainers/community/google/pubsub.py @@ -11,11 +11,14 @@ # License for the specific language governing permissions and limitations # under the License. import os +from typing import TypeVar from unittest.mock import patch +from google.cloud import pubsub + from testcontainers.core.container import DockerContainer -from google.cloud import pubsub +T = TypeVar("T") class PubSubContainer(DockerContainer): @@ -52,18 +55,18 @@ def __init__( def get_pubsub_emulator_host(self) -> str: return f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port)}" - def _get_client(self, cls: type, **kwargs) -> dict: + def _get_client(self, cls: type[T], **kwargs: object) -> T: with patch.dict(os.environ, PUBSUB_EMULATOR_HOST=self.get_pubsub_emulator_host()): return cls(**kwargs) - def get_publisher_client(self, **kwargs) -> pubsub.PublisherClient: + def get_publisher_client(self, **kwargs: object) -> pubsub.PublisherClient: from google.auth import credentials kwargs["client_options"] = {"api_endpoint": self.get_pubsub_emulator_host()} kwargs["credentials"] = credentials.AnonymousCredentials() return self._get_client(pubsub.PublisherClient, **kwargs) - def get_subscriber_client(self, **kwargs) -> pubsub.SubscriberClient: + def get_subscriber_client(self, **kwargs: object) -> pubsub.SubscriberClient: from google.auth import credentials kwargs["client_options"] = {"api_endpoint": self.get_pubsub_emulator_host()} diff --git a/src/testcontainers/community/influxdb/__init__.py b/src/testcontainers/community/influxdb/__init__.py index d8956e992..4c4ac46ec 100644 --- a/src/testcontainers/community/influxdb/__init__.py +++ b/src/testcontainers/community/influxdb/__init__.py @@ -54,7 +54,7 @@ def __init__( # specifies the port on the host machine where influxdb is exposed; a random available port otherwise host_port: Optional[int] = None, **docker_client_kw, - ): + ) -> None: super().__init__(image=image, **docker_client_kw) self.container_port = container_port self.host_port = host_port diff --git a/src/testcontainers/community/influxdb1/__init__.py b/src/testcontainers/community/influxdb1/__init__.py index a516b6adc..8cabf2bc6 100644 --- a/src/testcontainers/community/influxdb1/__init__.py +++ b/src/testcontainers/community/influxdb1/__init__.py @@ -14,6 +14,7 @@ from typing import Optional from influxdb import InfluxDBClient + from testcontainers.community.influxdb import InfluxDbContainer @@ -40,7 +41,7 @@ def __init__( # specifies the port on the host machine where influxdb is exposed; a random available port otherwise host_port: Optional[int] = None, **docker_client_kw, - ): + ) -> None: super().__init__(image, container_port, host_port, **docker_client_kw) def get_client(self, **client_kwargs): diff --git a/src/testcontainers/community/influxdb2/__init__.py b/src/testcontainers/community/influxdb2/__init__.py index 1dbb83676..6226e8c43 100644 --- a/src/testcontainers/community/influxdb2/__init__.py +++ b/src/testcontainers/community/influxdb2/__init__.py @@ -15,6 +15,7 @@ from typing import Optional from influxdb_client import InfluxDBClient, Organization + from testcontainers.community.influxdb import InfluxDbContainer @@ -52,7 +53,7 @@ def __init__( bucket: Optional[str] = None, retention: Optional[str] = None, **docker_client_kw, - ): + ) -> None: super().__init__(image, container_port, host_port, **docker_client_kw) configuration = { diff --git a/src/testcontainers/community/k3s/__init__.py b/src/testcontainers/community/k3s/__init__.py index 02a3be693..fecdd50c0 100644 --- a/src/testcontainers/community/k3s/__init__.py +++ b/src/testcontainers/community/k3s/__init__.py @@ -39,7 +39,7 @@ class K3SContainer(DockerContainer): KUBE_SECURE_PORT = 6443 RANCHER_WEBHOOK_PORT = 8443 - def __init__(self, image="rancher/k3s:latest", enable_cgroup_mount=True, **kwargs) -> None: + def __init__(self, image="rancher/k3s:latest", enable_cgroup_mount=True, **kwargs: object) -> None: super().__init__(image, **kwargs) self.with_exposed_ports(self.KUBE_SECURE_PORT, self.RANCHER_WEBHOOK_PORT) self.with_env("K3S_URL", f"https://{self.get_container_host_ip()}:{self.KUBE_SECURE_PORT}") diff --git a/src/testcontainers/community/kafka/__init__.py b/src/testcontainers/community/kafka/__init__.py index ce5532ff0..5ed026a19 100644 --- a/src/testcontainers/community/kafka/__init__.py +++ b/src/testcontainers/community/kafka/__init__.py @@ -6,12 +6,13 @@ from os import environ from textwrap import dedent +from typing_extensions import Self + from testcontainers.community.kafka._redpanda import RedpandaContainer from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.version import ComparableVersion from testcontainers.core.wait_strategies import LogMessageWaitStrategy -from typing_extensions import Self __all__ = [ "KafkaContainer", diff --git a/src/testcontainers/community/keycloak/__init__.py b/src/testcontainers/community/keycloak/__init__.py index ef79f31a4..a517c45d6 100644 --- a/src/testcontainers/community/keycloak/__init__.py +++ b/src/testcontainers/community/keycloak/__init__.py @@ -14,11 +14,11 @@ from typing import Optional import requests +from keycloak import KeycloakAdmin + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs -from keycloak import KeycloakAdmin - _DEFAULT_DEV_COMMAND = "start-dev" # Since Keycloak v26.0.0 # See: https://www.keycloak.org/server/all-config#category-bootstrap_admin @@ -153,7 +153,7 @@ def with_realm_import_folder(self, realm_import_folder: str) -> "KeycloakContain self.has_realm_imports = True return self - def get_client(self, **kwargs) -> KeycloakAdmin: + def get_client(self, **kwargs: object) -> KeycloakAdmin: default_kwargs = { "server_url": self.get_url(), "username": self.username, diff --git a/src/testcontainers/community/localstack/__init__.py b/src/testcontainers/community/localstack/__init__.py index 77bc9512f..de05e2c13 100644 --- a/src/testcontainers/community/localstack/__init__.py +++ b/src/testcontainers/community/localstack/__init__.py @@ -15,6 +15,7 @@ from typing import Any, Optional import boto3 + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -74,7 +75,7 @@ def get_url(self) -> str: return f"http://{host}:{port}" @ft.wraps(boto3.client) - def get_client(self, name, **kwargs) -> Any: + def get_client(self, name, **kwargs: object) -> Any: kwargs_ = { "endpoint_url": self.get_url(), "region_name": self.region_name, diff --git a/src/testcontainers/community/mailpit/__init__.py b/src/testcontainers/community/mailpit/__init__.py index a95337719..26b802fad 100644 --- a/src/testcontainers/community/mailpit/__init__.py +++ b/src/testcontainers/community/mailpit/__init__.py @@ -24,6 +24,7 @@ NoEncryption, ) from cryptography.x509.oid import NameOID + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs diff --git a/src/testcontainers/community/memcached/__init__.py b/src/testcontainers/community/memcached/__init__.py index 75844873d..a68ad3b8e 100644 --- a/src/testcontainers/community/memcached/__init__.py +++ b/src/testcontainers/community/memcached/__init__.py @@ -34,7 +34,7 @@ class MemcachedContainer(DockerContainer): ... host, port = memcached_container.get_host_and_port() """ - def __init__(self, image="memcached:1", port_to_expose=11211, **kwargs): + def __init__(self, image="memcached:1", port_to_expose=11211, **kwargs: object) -> None: super().__init__(image, **kwargs) self.port_to_expose = port_to_expose self.with_exposed_ports(port_to_expose) diff --git a/src/testcontainers/community/minio/__init__.py b/src/testcontainers/community/minio/__init__.py index 63448f712..5310e3cd6 100644 --- a/src/testcontainers/community/minio/__init__.py +++ b/src/testcontainers/community/minio/__init__.py @@ -1,9 +1,9 @@ +from minio import Minio + from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import HttpWaitStrategy -from minio import Minio - class MinioContainer(DockerContainer): """ @@ -64,7 +64,7 @@ def __init__( self.with_env("MINIO_SECRET_KEY", self.secret_key) self.with_command(f"server /data --address :{self.port}") - def get_client(self, **kwargs) -> Minio: + def get_client(self, **kwargs: object) -> Minio: """Returns a Minio client to connect to the container. Returns: diff --git a/src/testcontainers/community/mongodb/__init__.py b/src/testcontainers/community/mongodb/__init__.py index eb7a8747d..53eda1971 100644 --- a/src/testcontainers/community/mongodb/__init__.py +++ b/src/testcontainers/community/mongodb/__init__.py @@ -15,6 +15,7 @@ from typing import Optional from pymongo import MongoClient + from testcontainers.core.generic import DbContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import HealthcheckWaitStrategy, LogMessageWaitStrategy diff --git a/src/testcontainers/community/mqtt/__init__.py b/src/testcontainers/community/mqtt/__init__.py index e20c8adcc..fece15bc2 100644 --- a/src/testcontainers/community/mqtt/__init__.py +++ b/src/testcontainers/community/mqtt/__init__.py @@ -14,9 +14,10 @@ from pathlib import Path from typing import TYPE_CHECKING, Optional +from typing_extensions import Self + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs -from typing_extensions import Self if TYPE_CHECKING: from paho.mqtt.client import Client @@ -74,7 +75,7 @@ def get_client(self) -> "Client": self.client = client return client - def new_client(self, **kwargs) -> tuple["Client", "MQTTErrorCode"]: + def new_client(self, **kwargs: object) -> tuple["Client", "MQTTErrorCode"]: """ Get a paho.mqtt client connected to this container. Check the returned object is_connected() method before use diff --git a/src/testcontainers/community/neo4j/__init__.py b/src/testcontainers/community/neo4j/__init__.py index b322e04ca..6ddaba970 100644 --- a/src/testcontainers/community/neo4j/__init__.py +++ b/src/testcontainers/community/neo4j/__init__.py @@ -14,13 +14,13 @@ import os from typing import Optional +from neo4j import Driver, GraphDatabase + from testcontainers.core.config import testcontainers_config as c from testcontainers.core.generic import DbContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.wait_strategies import LogMessageWaitStrategy -from neo4j import Driver, GraphDatabase - class Neo4jContainer(DbContainer): """ @@ -72,5 +72,5 @@ def _connect(self) -> None: # force them to do a round trip to confirm neo4j is working driver.verify_connectivity() - def get_driver(self, **kwargs) -> Driver: + def get_driver(self, **kwargs: object) -> Driver: return GraphDatabase.driver(self.get_connection_url(), auth=(self.username, self.password), **kwargs) diff --git a/src/testcontainers/community/nginx/__init__.py b/src/testcontainers/community/nginx/__init__.py index ecf4c072e..577ebf869 100644 --- a/src/testcontainers/community/nginx/__init__.py +++ b/src/testcontainers/community/nginx/__init__.py @@ -20,7 +20,7 @@ class NginxContainer(DockerContainer): - def __init__(self, image: str = "nginx:latest", port: int = 80, **kwargs) -> None: + def __init__(self, image: str = "nginx:latest", port: int = 80, **kwargs: object) -> None: raise_for_deprecated_parameter(kwargs, "port_to_expose", "port") super().__init__(image, **kwargs) self.port = port diff --git a/src/testcontainers/community/ollama/__init__.py b/src/testcontainers/community/ollama/__init__.py index 2093f00da..53a5ee589 100644 --- a/src/testcontainers/community/ollama/__init__.py +++ b/src/testcontainers/community/ollama/__init__.py @@ -16,6 +16,7 @@ from docker.types.containers import DeviceRequest from requests import get + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs @@ -91,7 +92,7 @@ def __init__( ollama_home: Optional[Union[str, PathLike]] = None, **kwargs, # - ): + ) -> None: super().__init__(image=image, **kwargs) self.ollama_home = ollama_home self.with_exposed_ports(OllamaContainer.OLLAMA_PORT) diff --git a/src/testcontainers/community/openfga/__init__.py b/src/testcontainers/community/openfga/__init__.py index ac0a4ad7c..bb97eceab 100644 --- a/src/testcontainers/community/openfga/__init__.py +++ b/src/testcontainers/community/openfga/__init__.py @@ -14,6 +14,7 @@ from typing import Optional import requests + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready diff --git a/src/testcontainers/community/opensearch/__init__.py b/src/testcontainers/community/opensearch/__init__.py index c1a4b3df3..48660da2b 100644 --- a/src/testcontainers/community/opensearch/__init__.py +++ b/src/testcontainers/community/opensearch/__init__.py @@ -2,10 +2,11 @@ from opensearchpy import OpenSearch from opensearchpy.exceptions import ConnectionError, TransportError +from urllib3.exceptions import ProtocolError + from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import wait_container_is_ready -from urllib3.exceptions import ProtocolError MIN_REQUIRED_INITIAL_ADMIN_PASSWORD = [2, 12, 0] @@ -84,7 +85,7 @@ def get_config(self) -> dict: "password": self.initial_admin_password, } - def get_client(self, verify_certs: bool = False, **kwargs) -> OpenSearch: + def get_client(self, verify_certs: bool = False, **kwargs: object) -> OpenSearch: """Returns a OpenSearch client to connect to the container. Returns: diff --git a/src/testcontainers/community/postgres/py.typed b/src/testcontainers/community/postgres/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/testcontainers/community/qdrant/__init__.py b/src/testcontainers/community/qdrant/__init__.py index 9341246cc..17dc65f98 100644 --- a/src/testcontainers/community/qdrant/__init__.py +++ b/src/testcontainers/community/qdrant/__init__.py @@ -59,7 +59,7 @@ def __init__( def _configure(self) -> None: self.with_env("QDRANT__SERVICE__API_KEY", self._api_key) - def get_client(self, **kwargs): + def get_client(self, **kwargs: object): """ Get a `qdrant_client.QdrantClient` instance associated with the container. @@ -84,7 +84,7 @@ def get_client(self, **kwargs): **kwargs, ) - def get_async_client(self, **kwargs): + def get_async_client(self, **kwargs: object): """ Get a `qdrant_client.AsyncQdrantClient` instance associated with the container. diff --git a/src/testcontainers/community/rabbitmq/__init__.py b/src/testcontainers/community/rabbitmq/__init__.py index d86de1214..66d2d35f3 100644 --- a/src/testcontainers/community/rabbitmq/__init__.py +++ b/src/testcontainers/community/rabbitmq/__init__.py @@ -2,6 +2,7 @@ from typing import Optional import pika + from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready diff --git a/src/testcontainers/community/redis/__init__.py b/src/testcontainers/community/redis/__init__.py index 0cfd9be56..e1f55bb3c 100644 --- a/src/testcontainers/community/redis/__init__.py +++ b/src/testcontainers/community/redis/__init__.py @@ -13,13 +13,13 @@ from typing import Optional +import redis +from redis.asyncio import Redis as asyncRedis + from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter from testcontainers.core.waiting_utils import WaitStrategy, WaitStrategyTarget -import redis -from redis.asyncio import Redis as asyncRedis - class RedisContainer(DockerContainer): """ @@ -35,7 +35,9 @@ class RedisContainer(DockerContainer): ... redis_client = redis_container.get_client() """ - def __init__(self, image: str = "redis:latest", port: int = 6379, password: Optional[str] = None, **kwargs) -> None: + def __init__( + self, image: str = "redis:latest", port: int = 6379, password: Optional[str] = None, **kwargs: object + ) -> None: raise_for_deprecated_parameter(kwargs, "port_to_expose", "port") super().__init__(image, _wait_strategy=PingWaitStrategy(), **kwargs) self.port = port @@ -44,7 +46,7 @@ def __init__(self, image: str = "redis:latest", port: int = 6379, password: Opti if self.password: self.with_command(f"redis-server --requirepass {self.password}") - def get_client(self, **kwargs) -> redis.Redis: + def get_client(self, **kwargs: object) -> redis.Redis: """ Get a redis client. @@ -86,10 +88,10 @@ class AsyncRedisContainer(RedisContainer): ... redis_client =await redis_container.get_async_client() """ - def __init__(self, image="redis:latest", port_to_expose=6379, password=None, **kwargs): + def __init__(self, image="redis:latest", port_to_expose=6379, password=None, **kwargs: object) -> None: super().__init__(image, port_to_expose, password, **kwargs) - async def get_async_client(self, **kwargs): + async def get_async_client(self, **kwargs: object): return await asyncRedis( host=self.get_container_host_ip(), port=self.get_exposed_port(self.port), diff --git a/src/testcontainers/community/scylla/__init__.py b/src/testcontainers/community/scylla/__init__.py index ca634fd62..6e4e0eaba 100644 --- a/src/testcontainers/community/scylla/__init__.py +++ b/src/testcontainers/community/scylla/__init__.py @@ -20,7 +20,7 @@ class ScyllaContainer(DockerContainer): ... "= {'class': 'SimpleStrategy', 'replication_factor': '1'};") """ - def __init__(self, image="scylladb/scylla:latest", ports_to_expose=(9042,)): + def __init__(self, image="scylladb/scylla:latest", ports_to_expose=(9042,)) -> None: super().__init__(image) self.ports_to_expose = ports_to_expose self.with_exposed_ports(*self.ports_to_expose) @@ -37,7 +37,7 @@ def start(self): self._connect() return self - def get_cluster(self, **kwargs): + def get_cluster(self, **kwargs: object): from cassandra.cluster import Cluster hostname = self.get_container_host_ip() diff --git a/src/testcontainers/community/selenium/__init__.py b/src/testcontainers/community/selenium/__init__.py index 2403cb7b6..e99b4aa83 100644 --- a/src/testcontainers/community/selenium/__init__.py +++ b/src/testcontainers/community/selenium/__init__.py @@ -14,14 +14,14 @@ from typing import Any, Optional import urllib3 +from selenium import webdriver +from selenium.webdriver.common.options import ArgOptions +from typing_extensions import Self + from testcontainers.community.selenium.video import SeleniumVideoContainer from testcontainers.core.container import DockerContainer from testcontainers.core.network import Network from testcontainers.core.waiting_utils import wait_container_is_ready -from typing_extensions import Self - -from selenium import webdriver -from selenium.webdriver.common.options import ArgOptions IMAGES = {"firefox": "selenium/standalone-firefox:latest", "chrome": "selenium/standalone-chrome:latest"} diff --git a/src/testcontainers/community/selenium/video.py b/src/testcontainers/community/selenium/video.py index debf098db..c28cda0a8 100644 --- a/src/testcontainers/community/selenium/video.py +++ b/src/testcontainers/community/selenium/video.py @@ -22,7 +22,7 @@ class SeleniumVideoContainer(DockerContainer): Selenium video container. """ - def __init__(self, image: Optional[str] = None, **kwargs) -> None: + def __init__(self, image: Optional[str] = None, **kwargs: object) -> None: self.image = image or VIDEO_DEFAULT_IMAGE super().__init__(image=self.image, **kwargs) diff --git a/src/testcontainers/community/sftp/__init__.py b/src/testcontainers/community/sftp/__init__.py index a3c46f9f1..ae299fcde 100644 --- a/src/testcontainers/community/sftp/__init__.py +++ b/src/testcontainers/community/sftp/__init__.py @@ -18,6 +18,7 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa + from testcontainers.core.container import DockerContainer from testcontainers.core.wait_strategies import LogMessageWaitStrategy diff --git a/src/testcontainers/community/sftp/py.typed b/src/testcontainers/community/sftp/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/testcontainers/community/trino/__init__.py b/src/testcontainers/community/trino/__init__.py index 4e2b1f83c..9b2503d6e 100644 --- a/src/testcontainers/community/trino/__init__.py +++ b/src/testcontainers/community/trino/__init__.py @@ -26,7 +26,7 @@ def __init__( container_start_timeout: int = 60, wait_strategy_check_string: str = ".*======== SERVER STARTED ========.*", **kwargs, - ): + ) -> None: super().__init__(image=image, **kwargs) self.user = user self.port = port diff --git a/src/testcontainers/community/valkey/__init__.py b/src/testcontainers/community/valkey/__init__.py index 7237a64fe..657728210 100644 --- a/src/testcontainers/community/valkey/__init__.py +++ b/src/testcontainers/community/valkey/__init__.py @@ -24,7 +24,7 @@ class ValkeyContainer(DockerContainer): """ - def __init__(self, image: str = f"{_BASE_IMAGE}:latest", port: int = 6379, **kwargs) -> None: + def __init__(self, image: str = f"{_BASE_IMAGE}:latest", port: int = 6379, **kwargs: object) -> None: super().__init__(image, **kwargs) self.port = port self.password: str | None = None diff --git a/src/testcontainers/community/weaviate/__init__.py b/src/testcontainers/community/weaviate/__init__.py index c8c95bb3a..0379582d1 100644 --- a/src/testcontainers/community/weaviate/__init__.py +++ b/src/testcontainers/community/weaviate/__init__.py @@ -14,10 +14,11 @@ from typing import Optional from docker.types import IPAMConfig, IPAMPool +from typing_extensions import Self + from testcontainers.core.container import DockerContainer from testcontainers.core.network import Network from testcontainers.core.wait_strategies import HttpWaitStrategy -from typing_extensions import Self class WeaviateContainer(DockerContainer): diff --git a/src/testcontainers/core/config.py b/src/testcontainers/core/config.py index bc9be49fa..f5a9afc9f 100644 --- a/src/testcontainers/core/config.py +++ b/src/testcontainers/core/config.py @@ -40,7 +40,8 @@ def get_docker_socket() -> str: try: client = docker.from_env() - socket_path = client.api.get_adapter(client.api.base_url).socket_path + # if adapter does not define socket_path, we return default anyway, so ignore types + socket_path = client.api.get_adapter(client.api.base_url).socket_path # type: ignore[attr-defined] # return the normalized path as string return str(Path(socket_path).absolute()) except Exception: diff --git a/src/testcontainers/core/container.py b/src/testcontainers/core/container.py index e16ff2447..a4330a5f2 100644 --- a/src/testcontainers/core/container.py +++ b/src/testcontainers/core/container.py @@ -4,6 +4,7 @@ import pathlib import sys import tarfile +from collections.abc import Mapping from os import PathLike from socket import socket from types import TracebackType @@ -11,6 +12,7 @@ import docker.errors from docker import version +from docker.errors import APIError from docker.models.containers import ExecResult from docker.types import EndpointConfig from dotenv import dotenv_values @@ -39,6 +41,11 @@ class Mount(TypedDict): mode: str +class BytesExecResult(ExecResult): + exit_code: int | None + output: bytes + + class DockerContainer: """ @@ -80,7 +87,7 @@ def __init__( ) -> None: self.env = env or {} - self.ports: dict[Union[str, int], Optional[Union[str, int]]] = {} + self.ports: dict[str, Optional[int]] = {} if ports: self.with_exposed_ports(*ports) @@ -142,7 +149,28 @@ def with_bind_ports(self, container: Union[str, int], host: Optional[Union[str, >>> container = container.with_bind_ports("8081/tcp", 8081) """ - self.ports[container] = host + container = str(container) + host_protocol = "" + host_port: Optional[int] = None + + # normalize the host port to fit the typing protocol and podman api + if isinstance(host, int): + host_port = host + elif host: + host_port_str, _, host_protocol = host.partition("/") + try: + host_port = int(host_port_str) + except ValueError: + raise APIError(f"Host port '{host_port_str}' of '{host}' is not an integer.") from None + + if host_protocol: + container_port, _, container_protocol = container.partition("/") + if not container_protocol: + container = f"{container_port}/{host_protocol}" + elif container_protocol != host_protocol: + raise APIError(f"Container protocol {container_protocol} does not match host protocol {host_protocol}") + + self.ports[container] = host_port return self def with_exposed_ports(self, *ports: Union[str, int]) -> Self: @@ -160,7 +188,7 @@ def with_exposed_ports(self, *ports: Union[str, int]) -> Self: """ for port in ports: - self.ports[port] = None + self.ports[str(port)] = None return self def with_network(self, network: Network) -> Self: @@ -208,9 +236,10 @@ def start(self) -> Self: self.image, command=self._command, environment=self.env, - ports=cast("dict[int, Optional[int]]", self.ports), + ports=self.ports, name=self._name, - volumes=self.volumes, + # dict is invariant: expanding fields explicitly lets mypy verify each value type. + volumes={host: {"bind": mount["bind"], "mode": mount["mode"]} for host, mount in self.volumes.items()}, tmpfs=self.tmpfs, **{**network_kwargs, **self._kwargs}, ) @@ -249,6 +278,11 @@ def __exit__( ) -> None: self.stop() + def get_container_id(self) -> str: + if self._container is None or self._container.id is None: + raise ContainerStartException("Container is not started") + return self._container.id + def get_container_host_ip(self) -> str: connection_mode: ConnectionMode connection_mode = self.get_docker_client().get_connection_mode() @@ -256,15 +290,9 @@ def get_container_host_ip(self) -> str: if connection_mode == ConnectionMode.docker_host: return self.get_docker_client().host() elif connection_mode == ConnectionMode.gateway_ip: - # mypy: - container = self._container - assert container is not None - return self.get_docker_client().gateway_ip(container.id) + return self.get_docker_client().gateway_ip(self.get_container_id()) elif connection_mode == ConnectionMode.bridge_ip: - # mypy: - container = self._container - assert container is not None - return self.get_docker_client().bridge_ip(container.id) + return self.get_docker_client().bridge_ip(self.get_container_id()) else: # ensure that we covered all possible connection_modes assert_never(connection_mode) @@ -277,9 +305,7 @@ def get_exposed_port(self, port: int) -> int: def _get_exposed_port(self, port: int) -> int: if self.get_docker_client().get_connection_mode().use_mapped_port: - c = self._container - assert c is not None - return int(self.get_docker_client().port(c.id, port)) + return int(self.get_docker_client().port(self.get_container_id(), port)) return port def with_command(self, command: Union[str, list[str]]) -> Self: @@ -306,6 +332,8 @@ def with_tmpfs_mount(self, container_path: str, size: Optional[str] = None) -> S return self def get_wrapped_container(self) -> "Container": + if self._container is None: + raise ContainerStartException("Container is not started") return self._container def get_docker_client(self) -> DockerClient: @@ -329,12 +357,14 @@ def status(self) -> str: """Get container status for compatibility with wait strategies.""" if not self._container: return "not_started" - return cast("str", self._container.status) + return self._container.status - def exec(self, command: Union[str, list[str]]) -> ExecResult: + def exec(self, command: Union[str, list[str]]) -> BytesExecResult: if not self._container: raise ContainerStartException("Container should be started before executing a command") - return self._container.exec_run(command) + result = self._container.exec_run(command) + assert isinstance(result.output, bytes) + return BytesExecResult(result[0], result[1]) def wait(self) -> int: """Wait for the container to stop and return its exit code.""" @@ -352,7 +382,7 @@ def get_container_info(self) -> Optional[ContainerInspectInfo]: if self._cached_container_info is not None: return self._cached_container_info - if not self._container: + if not self._container or self._container.id is None: return None try: diff --git a/src/testcontainers/core/docker_client.py b/src/testcontainers/core/docker_client.py index 288b27c79..780c83790 100644 --- a/src/testcontainers/core/docker_client.py +++ b/src/testcontainers/core/docker_client.py @@ -10,22 +10,24 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from __future__ import annotations + import contextlib -import functools as ft +import functools import importlib.metadata import ipaddress import os import socket import urllib import urllib.parse -from collections.abc import Iterable -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, cast +from collections.abc import Iterable, Iterator, Mapping +from typing import TYPE_CHECKING, Any, Literal, Optional, TypedDict, Union, cast, overload import docker from docker.context import ContextAPI -from docker.models.containers import Container, ContainerCollection -from docker.models.images import Image, ImageCollection -from typing_extensions import ParamSpec +from docker.models.containers import Container +from docker.models.images import Image +from typing_extensions import Unpack from testcontainers.core import utils from testcontainers.core.auth import DockerAuthInfo, parse_docker_auth_config @@ -35,28 +37,140 @@ from testcontainers.core.labels import SESSION_ID, create_labels if TYPE_CHECKING: - from docker.models.networks import Network as DockerNetwork - -LOGGER = utils.setup_logger(__name__) + from io import StringIO + from typing import IO -_P = ParamSpec("_P") -_T = TypeVar("_T") + from docker._types import JSON, ContainerWeightDevice # only exists in docker-stubs, not at runtime + from docker.models.containers import _RestartPolicy + from docker.models.images import _ContainerLimits + from docker.models.networks import Network as DockerNetwork + from docker.types import EndpointConfig + from docker.types.containers import DeviceRequest, LogConfig, Ulimit + from docker.types.services import Mount + class _RunKwargs(TypedDict, total=False): + """Extra keyword arguments for :meth:`DockerClient.run`. -def _wrapped_container_collection(function: Callable[_P, _T]) -> Callable[_P, _T]: - @ft.wraps(ContainerCollection.run) - def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T: - return function(*args, **kwargs) + Mirrors the parameters of :meth:`docker.models.containers.ContainerCollection.run` + that are not declared explicitly on :meth:`DockerClient.run`. All fields are optional + (``total=False``); omitting a field lets the Docker daemon apply its default. + """ - return wrapper + auto_remove: bool + blkio_weight_device: Optional[list[ContainerWeightDevice]] + blkio_weight: Optional[int] + cap_add: Optional[list[str]] + cap_drop: Optional[list[str]] + cgroup_parent: Optional[str] + cgroupns: Optional[Literal["private", "host"]] + cpu_count: Optional[int] + cpu_percent: Optional[int] + cpu_period: Optional[int] + cpu_quota: Optional[int] + cpu_rt_period: Optional[int] + cpu_rt_runtime: Optional[int] + cpu_shares: Optional[int] + cpuset_cpus: Optional[str] + cpuset_mems: Optional[str] + device_cgroup_rules: Optional[list[str]] + device_read_bps: Optional[list[Mapping[str, Union[str, int]]]] + device_read_iops: Optional[list[Mapping[str, Union[str, int]]]] + device_write_bps: Optional[list[Mapping[str, Union[str, int]]]] + device_write_iops: Optional[list[Mapping[str, Union[str, int]]]] + devices: Optional[list[str]] + device_requests: Optional[list[DeviceRequest]] + dns: Optional[list[str]] + dns_opt: Optional[list[str]] + dns_search: Optional[list[str]] + domainname: Optional[Union[str, list[str]]] + entrypoint: Optional[Union[str, list[str]]] + extra_hosts: Optional[dict[str, str]] + group_add: Optional[Iterable[Union[str, int]]] + healthcheck: Optional[dict[str, Any]] + hostname: Optional[str] + init: Optional[bool] + init_path: Optional[str] + ipc_mode: Optional[str] + isolation: Optional[str] + kernel_memory: Optional[Union[str, int]] + links: Optional[ + Union[dict[str, str], dict[str, None], dict[str, Union[str, None]], Iterable[tuple[str, Union[str, None]]]] + ] + log_config: Optional[LogConfig] + lxc_conf: Optional[dict[str, str]] + mac_address: Optional[str] + mem_limit: Optional[Union[str, int]] + mem_reservation: Optional[Union[str, int]] + mem_swappiness: Optional[int] + memswap_limit: Optional[Union[str, int]] + mounts: Optional[list[Mount]] + name: Optional[str] + nano_cpus: Optional[int] + network: Optional[str] + network_disabled: bool + network_mode: Optional[str] + networking_config: Optional[dict[str, EndpointConfig]] + oom_kill_disable: bool + oom_score_adj: Optional[int] + pid_mode: Optional[str] + pids_limit: Optional[int] + platform: Optional[str] + privileged: bool + publish_all_ports: bool + read_only: Optional[bool] + restart_policy: Optional[_RestartPolicy] + runtime: Optional[str] + security_opt: Optional[list[str]] + shm_size: Optional[Union[str, int]] + stdin_open: bool + stop_signal: Optional[str] + storage_opt: Optional[dict[str, str]] + stream: bool + sysctls: Optional[dict[str, str]] + tmpfs: Optional[dict[str, str]] + tty: bool + ulimits: Optional[list[Ulimit]] + use_config_proxy: Optional[bool] + user: Optional[Union[str, int]] + userns_mode: Optional[str] + uts_mode: Optional[str] + version: Optional[str] + volume_driver: Optional[str] + volumes: Optional[Union[dict[str, dict[str, str]], list[str]]] + volumes_from: Optional[list[str]] + working_dir: Optional[str] + + class _BuildKwargs(TypedDict, total=False): + """Extra keyword arguments for :meth:`DockerClient.build`. + + Mirrors the parameters of :meth:`docker.models.images.ImageCollection.build` + that are not declared explicitly on :meth:`DockerClient.build`. All fields are optional; + """ + fileobj: Union[StringIO, IO[bytes]] + quiet: bool + nocache: bool + timeout: int + custom_context: bool + encoding: str + pull: bool + forcerm: bool + dockerfile: str + buildargs: dict[str, Any] + container_limits: _ContainerLimits + shmsize: int + labels: dict[str, Any] + cache_from: list[str] + target: str + network_mode: str + squash: bool + extra_hosts: Union[list[str], dict[str, str]] + platform: str + isolation: str + use_config_proxy: bool -def _wrapped_image_collection(function: Callable[_P, _T]) -> Callable[_P, _T]: - @ft.wraps(ImageCollection.build) - def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T: - return function(*args, **kwargs) - return wrapper +LOGGER = utils.setup_logger(__name__) class DockerClient: @@ -64,6 +178,8 @@ class DockerClient: Thin wrapper around :class:`docker.DockerClient` for a more functional interface. """ + client: docker.DockerClient + def __init__(self, **kwargs: Any) -> None: docker_host = get_docker_host() @@ -85,49 +201,94 @@ def __init__(self, **kwargs: Any) -> None: if auth_config := parse_docker_auth_config(docker_auth_config): self.login(auth_config[0]) # Only using the first auth config) - @_wrapped_container_collection + @overload def run( self, image: str, command: Optional[Union[str, list[str]]] = None, - environment: Optional[dict[str, str]] = None, - ports: Optional[dict[int, Optional[int]]] = None, + *, + detach: Literal[True], + environment: Optional[Union[dict[str, str], list[str]]] = None, + ports: Optional[Mapping[str, Union[int, list[int], tuple[str, int], None]]] = None, labels: Optional[dict[str, str]] = None, + stdout: bool = True, + stderr: bool = False, + remove: bool = False, + **kwargs: Unpack[_RunKwargs], + ) -> Container: ... + + @overload + def run( + self, + image: str, + command: Optional[Union[str, list[str]]] = None, + *, + detach: Literal[False] = False, + environment: Optional[Union[dict[str, str], list[str]]] = None, + ports: Optional[Mapping[str, Union[int, list[int], tuple[str, int], None]]] = None, + labels: Optional[dict[str, str]] = None, + stdout: bool = True, + stderr: bool = False, + remove: bool = False, + **kwargs: Unpack[_RunKwargs], + ) -> bytes: ... + + def run( + self, + image: str, + command: Optional[Union[str, list[str]]] = None, + *, detach: bool = False, + environment: Optional[Union[dict[str, str], list[str]]] = None, + ports: Optional[Mapping[str, Union[int, list[int], tuple[str, int], None]]] = None, + labels: Optional[dict[str, str]] = None, stdout: bool = True, stderr: bool = False, remove: bool = False, - **kwargs: Any, - ) -> Container: + **kwargs: Unpack[_RunKwargs], + ) -> Union[Container, bytes]: # If the user has specified a network, we'll assume the user knows best if "network" not in kwargs and not get_docker_host(): # Otherwise we'll try to find the docker host for dind usage. host_network = self.find_host_network() if host_network: kwargs["network"] = host_network - container = self.client.containers.run( - image, - command=command, - stdout=stdout, - stderr=stderr, - remove=remove, - detach=detach, - environment=environment, - ports=ports, - labels=create_labels(image, labels), - **kwargs, - ) - return container + if detach: + return self.client.containers.run( + image, + command=command, + stdout=stdout, + stderr=stderr, + remove=remove, + detach=True, + environment=environment, + ports=ports, + labels=create_labels(image, labels), + **kwargs, + ) + else: + return self.client.containers.run( + image, + command=command, + stdout=stdout, + stderr=stderr, + remove=remove, + detach=False, + environment=environment, + ports=ports, + labels=create_labels(image, labels), + **kwargs, + ) - @_wrapped_container_collection def create( self, image: str, command: Optional[Union[str, list[str]]] = None, environment: Optional[dict[str, str]] = None, - ports: Optional[dict[int, Optional[int]]] = None, + ports: Optional[Mapping[str, Union[int, list[int], tuple[str, int], None]]] = None, labels: Optional[dict[str, str]] = None, - **kwargs: Any, + detach: bool = False, + **kwargs: Unpack[_RunKwargs], ) -> Container: """Create a container without starting it, pulling the image first if not present locally.""" if "network" not in kwargs and not get_docker_host(): @@ -147,19 +308,18 @@ def create( environment=environment, ports=ports, labels=create_labels(image, labels), + detach=detach, **kwargs, ) return container - @_wrapped_container_collection def start(self, container: Container) -> None: """Start a previously created container.""" container.start() - @_wrapped_image_collection def build( - self, path: str, tag: Optional[str], rm: bool = True, **kwargs: Any - ) -> tuple[Image, Iterable[dict[str, Any]]]: + self, path: str, tag: Optional[str] = None, rm: bool = True, **kwargs: Unpack[_BuildKwargs] + ) -> tuple[Image, Iterator[JSON]]: """ Build a Docker image from a directory containing the Dockerfile. @@ -345,7 +505,7 @@ def _get_docker_host_from_context() -> Optional[str]: # docker-py fall back to its own defaults in that case. if not host or context.Name == "default": return None - return cast("str", host) + return host def get_docker_host_hostname() -> Optional[str]: @@ -366,7 +526,7 @@ def is_ssh_docker_host() -> bool: return get_docker_host_hostname() is not None -@ft.lru_cache(maxsize=1) +@functools.lru_cache(maxsize=1) def is_podman() -> bool: """Detect whether the configured Docker daemon is actually Podman. diff --git a/src/testcontainers/core/image.py b/src/testcontainers/core/image.py index f4e6132d5..4654694ab 100644 --- a/src/testcontainers/core/image.py +++ b/src/testcontainers/core/image.py @@ -1,6 +1,8 @@ +from __future__ import annotations + from os import PathLike from types import TracebackType -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast import docker.errors from typing_extensions import Self @@ -11,6 +13,7 @@ if TYPE_CHECKING: from collections.abc import Iterable + from docker._types import JSON # only exists in docker-stubs, not at runtime from docker.models.images import Image logger = setup_logger(__name__) @@ -42,7 +45,7 @@ def __init__( docker_client_kw: Optional[dict[str, Any]] = None, tag: Optional[str] = None, clean_up: bool = True, - dockerfile_path: Union[str, PathLike[str]] = "Dockerfile", + dockerfile_path: str = "Dockerfile", no_cache: bool = False, **kwargs: Any, ) -> None: @@ -52,7 +55,7 @@ def __init__( self.clean_up = clean_up self._kwargs = kwargs self._image: Optional[Image] = None - self._logs: Optional[Iterable[dict[str, Any]]] = None + self._logs: Iterator[JSON] | None = None self._dockerfile_path = dockerfile_path self._no_cache = no_cache @@ -104,14 +107,14 @@ def __exit__( ) -> None: self.remove() - def get_wrapped_image(self) -> "Image": + def get_wrapped_image(self) -> Image: assert self._image is not None return self._image def get_docker_client(self) -> DockerClient: return self._docker - def get_logs(self) -> list[dict[str, Any]]: + def get_logs(self) -> list[JSON]: logs = self._logs if logs is None: return [] diff --git a/src/testcontainers/core/network.py b/src/testcontainers/core/network.py index f602a6eaa..167562069 100644 --- a/src/testcontainers/core/network.py +++ b/src/testcontainers/core/network.py @@ -27,7 +27,7 @@ class Network: def __init__( self, docker_client_kw: Optional[dict[str, Any]] = None, docker_network_kw: Optional[dict[str, Any]] = None - ): + ) -> None: self.name = str(uuid.uuid4()) self._docker = DockerClient(**(docker_client_kw or {})) self._docker_network_kw = docker_network_kw or {} diff --git a/src/testcontainers/core/wait_strategies.py b/src/testcontainers/core/wait_strategies.py index f2c2d4778..3b5975fdf 100644 --- a/src/testcontainers/core/wait_strategies.py +++ b/src/testcontainers/core/wait_strategies.py @@ -35,7 +35,7 @@ import time from datetime import timedelta from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Callable, Optional, Union from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen @@ -699,7 +699,7 @@ def _get_status_tc_container(container: "DockerContainer") -> str: logger.debug("fetching status of container %s", container) wrapped = container.get_wrapped_container() wrapped.reload() - return cast("str", wrapped.status) + return wrapped.status @staticmethod def _get_status_compose_container(container: DockerCompose) -> str: diff --git a/src/testcontainers/core/waiting_utils.py b/src/testcontainers/core/waiting_utils.py index 4b1b3de5a..c6241bb48 100644 --- a/src/testcontainers/core/waiting_utils.py +++ b/src/testcontainers/core/waiting_utils.py @@ -161,7 +161,7 @@ def check_http(container): ) class LegacyWaitStrategy(WaitStrategy): - def __init__(self, func: Callable[..., Any], instance: Any, args: tuple[Any], kwargs: dict[str, Any]): + def __init__(self, func: Callable[..., Any], instance: Any, args: tuple[Any], kwargs: dict[str, Any]) -> None: super().__init__() self.func = func self.instance = instance diff --git a/src/testcontainers/community/mailpit/py.typed b/src/testcontainers/py.typed similarity index 100% rename from src/testcontainers/community/mailpit/py.typed rename to src/testcontainers/py.typed diff --git a/tests/community/generic/test_server.py b/tests/community/generic/test_server.py index 81f4e1dd1..66f0073e2 100644 --- a/tests/community/generic/test_server.py +++ b/tests/community/generic/test_server.py @@ -28,7 +28,7 @@ def test_server_container(test_image_tag: Optional[str], test_image_cleanup: boo image_build_logs = docker_image.get_logs() # check if dict is in any of the logs assert {"stream": f"Step 2/3 : EXPOSE {port}"} in image_build_logs, "Image logs mismatch" - assert (port, None) in srv.ports.items(), "Port mismatch" + assert (str(port), None) in srv.ports.items(), "Port mismatch" with pytest.raises(NotImplementedError): srv.get_api_url() test_url = srv._create_connection_url() diff --git a/tests/core/test_container.py b/tests/core/test_container.py index aa2f66c9b..3b08779a6 100644 --- a/tests/core/test_container.py +++ b/tests/core/test_container.py @@ -89,7 +89,7 @@ def test_get_exposed_port_original(container: DockerContainer, monkeypatch: pyte ("command", "ps", "_command", "ps"), ("env", {"e1": "v1"}, "env", {"e1": "v1"}), ("name", "foo-bar", "_name", "foo-bar"), - ("ports", [22, 80], "ports", {22: None, 80: None}), + ("ports", [22, 80], "ports", {"22": None, "80": None}), ( "volumes", [("/tmp", "/tmp2", "ro")], diff --git a/tests/core/test_core.py b/tests/core/test_core.py index 9312b0bca..3ae86ddf2 100644 --- a/tests/core/test_core.py +++ b/tests/core/test_core.py @@ -39,7 +39,9 @@ def test_docker_container_with_env_file(): container = DockerContainer("alpine").with_command("tail -f /dev/null") # Keep the container running container.with_env_file(env_file_path) # Load the environment variables from the file with container: - output = container.exec("env").output.decode("utf-8").strip() + exec_output = container.exec("env").output + assert isinstance(exec_output, bytes) + output = exec_output.decode("utf-8").strip() assert "TEST_ENV_VAR=hello" in output assert "NUMBER=123" in output assert "DOMAIN=example.org" in output diff --git a/tests/core/test_core_ports.py b/tests/core/test_core_ports.py index e22ad6108..b72075d7d 100644 --- a/tests/core/test_core_ports.py +++ b/tests/core/test_core_ports.py @@ -8,35 +8,23 @@ @pytest.mark.parametrize( - "container_port, host_port", + "container_port, host_port, expected_container_port", [ - ("8080", "8080"), - pytest.param( - "8125/udp", - "8125/udp", - marks=pytest.mark.skipif(is_podman(), reason="Podman rejects protocol in host_port"), - ), - pytest.param( - "8092/udp", - "8092/udp", - marks=pytest.mark.skipif(is_podman(), reason="Podman rejects protocol in host_port"), - ), - pytest.param( - "9000/tcp", - "9000/tcp", - marks=pytest.mark.skipif(is_podman(), reason="Podman rejects protocol in host_port"), - ), - pytest.param( - "8080", "8080/udp", marks=pytest.mark.skipif(is_podman(), reason="Podman rejects protocol in host_port") - ), - (8080, 8080), - (9000, None), - ("9009", None), - ("9000", ""), - ("9000/udp", ""), + ("8080", "8080", "8080/tcp"), + ("8125/udp", "8125/udp", "8125/udp"), + ("8092/udp", "8092/udp", "8092/udp"), + ("9000/tcp", "9000/tcp", "9000/tcp"), + ("8080", "8080/udp", "8080/udp"), + (8080, 8080, "8080/tcp"), + (9000, None, "9000/tcp"), + ("9009", None, "9009/tcp"), + ("9000", "", "9000/tcp"), + ("9000/udp", "", "9000/udp"), ], ) -def test_docker_container_with_bind_ports(container_port: Union[str, int], host_port: Optional[Union[str, int]]): +def test_docker_container_with_bind_ports( + container_port: Union[str, int], host_port: Optional[Union[str, int]], expected_container_port: str +): container = DockerContainer("alpine:latest") container.with_bind_ports(container_port, host_port) container.start() @@ -45,17 +33,13 @@ def test_docker_container_with_bind_ports(container_port: Union[str, int], host_ c_c = container._container assert c_c container_id = c_c.id + assert container_id is not None client = c_c.client + assert client is not None # assemble expected output to compare to container API - container_port = str(container_port) host_port = str(host_port or "") - - # if the port protocol is not specified, it will default to tcp - if "/" not in container_port: - container_port += "/tcp" - - expected = {container_port: [{"HostIp": "", "HostPort": host_port}]} + expected = {expected_container_port: [{"HostIp": "", "HostPort": host_port.partition("/")[0]}]} # compare PortBindings to expected output actual = client.containers.get(container_id).attrs["HostConfig"]["PortBindings"] @@ -81,6 +65,7 @@ def test_docker_container_with_bind_ports(container_port: Union[str, int], host_ (0, 0), (-1, 8080), (None, 8080), + ("8080/tcp", "8080/udp"), ], ) def test_error_docker_container_with_bind_ports(container_port: Union[str, int], host_port: Optional[Union[str, int]]): @@ -110,7 +95,9 @@ def test_docker_container_with_exposed_ports(ports: tuple[Union[str, int], ...], c_c = container._container assert c_c container_id = c_c.id + assert container_id is not None client = c_c.client + assert client is not None assert client.containers.get(container_id).attrs["Config"]["ExposedPorts"] == expected container.stop() diff --git a/tests/core/test_docker_client.py b/tests/core/test_docker_client.py index 6cea5b50b..34b449a9b 100644 --- a/tests/core/test_docker_client.py +++ b/tests/core/test_docker_client.py @@ -224,7 +224,7 @@ def test_find_host_network_found_by_docker_host(monkeypatch: pytest.MonkeyPatch) client = DockerClient() monkeypatch.setattr(client, "host", lambda: "172.22.0.1") - networks = [ + networks: list[dict[str, Any]] = [ # a network without IPAM {"Name": "host"}, # network with invalid subnet @@ -291,9 +291,9 @@ class ContainerRunFake: def __init__(self) -> None: self.calls: list[dict[str, Any]] = [] - def run(self, image: str, **kwargs: Any) -> str: + def run(self, image: str, **kwargs: Any) -> bytes: self.calls.append(kwargs) - return "CONTAINER" + return b"CONTAINER" class FakeClient: def __init__(self) -> None: @@ -304,7 +304,7 @@ def __init__(self) -> None: monkeypatch.setattr(client, "find_host_network", lambda: "new_bridge_network") monkeypatch.setattr(client, "client", fake_client) - assert client.run("test") == "CONTAINER" + assert client.run("test") == b"CONTAINER" assert fake_client.containers.calls[0]["network"] == "new_bridge_network" diff --git a/tests/core/test_docker_in_docker.py b/tests/core/test_docker_in_docker.py index be9703621..992d53ae1 100644 --- a/tests/core/test_docker_in_docker.py +++ b/tests/core/test_docker_in_docker.py @@ -40,6 +40,7 @@ def _wait_for_dind_return_ip(client: DockerClient, dind: Container): # get ip address for DOCKER_HOST # avoiding DockerContainer class here to prevent code changes affecting the test + assert dind.id is not None docker_host_ip = client.bridge_ip(dind.id) # Wait for startup timeout = 10 @@ -116,9 +117,11 @@ def test_dind_inherits_network(): ) as container: assert container.get_container_host_ip() == docker_host_ip # Check the gateways are the same, so they can talk to each other - assert container.get_docker_client().gateway_ip(container.get_wrapped_container().id) == client.gateway_ip( - not_really_dind.id - ) + wrapped_id = container.get_wrapped_container().id + assert wrapped_id is not None + dind_id = not_really_dind.id + assert dind_id is not None + assert container.get_docker_client().gateway_ip(wrapped_id) == client.gateway_ip(dind_id) wait_for_logs(container, "Hello from Docker!") stdout, stderr = container.get_logs() assert stdout, "There should be something on stdout" diff --git a/tests/core/test_image.py b/tests/core/test_image.py index 61534b885..3a4e51f9b 100644 --- a/tests/core/test_image.py +++ b/tests/core/test_image.py @@ -30,7 +30,13 @@ def test_docker_image(test_image_tag: Optional[str], test_cleanup: bool, check_f assert image.get_wrapped_image() is not None logs = image.get_logs() assert isinstance(logs, list), "Logs should be a list" - streams = [entry.get("stream", "").strip() for entry in logs] + streams = [] + for entry in logs: + if isinstance(entry, dict): + stream = entry.get("stream", "") + if isinstance(stream, str): + streams.append(stream) + assert any(s.upper().startswith("STEP 1/2") and "FROM ALPINE:LATEST" in s.upper() for s in streams) assert any(s.upper().startswith("STEP 2/2") and random_string in s for s in streams), ( f"Expected step 2 with '{random_string}' in logs: {streams}" @@ -38,6 +44,7 @@ def test_docker_image(test_image_tag: Optional[str], test_cleanup: bool, check_f with DockerContainer(str(image)) as container: c_c = container._container assert c_c + assert c_c.image is not None assert c_c.image.short_id.endswith(image_short_id), "Image ID mismatch" assert container.get_logs() == ((random_string + "\n").encode(), b""), "Container logs mismatch" @@ -70,6 +77,7 @@ def test_docker_image_with_custom_dockerfile_path(dockerfile_path: Optional[Path with DockerContainer(str(image)) as container: c_c = container._container assert c_c + assert c_c.image is not None assert c_c.image.short_id.endswith(image_short_id), "Image ID mismatch" assert container.get_logs() == (("Hello world!\n").encode(), b""), "Container logs mismatch" @@ -93,5 +101,6 @@ def test_docker_image_with_kwargs(): with DockerContainer(str(image)) as container: c_c = container._container assert c_c + assert c_c.image is not None assert c_c.image.short_id.endswith(image_short_id), "Image ID mismatch" assert container.get_logs() == (("new_arg\n").encode(), b""), "Container logs mismatch" diff --git a/tests/core/test_network.py b/tests/core/test_network.py index ecddec84b..0af690dcf 100644 --- a/tests/core/test_network.py +++ b/tests/core/test_network.py @@ -39,6 +39,7 @@ def test_network_create_errors(): with pytest.raises(docker.errors.APIError) as excinfo: network.create() + assert excinfo.value.response is not None assert excinfo.value.response.status_code == HTTPStatus.CONFLICT excinfo.match(f"network.*{network.name}.*already exists") network.remove() @@ -82,6 +83,7 @@ def assert_can_ping(container: DockerContainer, remote_name: str): def assert_can_request(container: DockerContainer, remote_name: str): status, output = container.exec(f"wget -qO- http://{remote_name}") assert status == 0 + assert isinstance(output, bytes) assert "Welcome to nginx!" in output.decode() @@ -89,7 +91,10 @@ def test_network_has_labels(): network = Network() try: network.create() - network = network._docker.client.networks.get(network_id=network.id) - assert LABEL_SESSION_ID in network.attrs.get("Labels") + assert network.id is not None + docker_network = network._docker.client.networks.get(network_id=network.id) + labels = docker_network.attrs.get("Labels") + assert labels is not None + assert LABEL_SESSION_ID in labels finally: network.remove() diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index e811ee396..4179242a3 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -3,7 +3,7 @@ import pytest from pytest import MonkeyPatch, raises, mark -from testcontainers.core import utils +import testcontainers.core.utils as utils def test_setup_logger() -> None: diff --git a/uv.lock b/uv.lock index 95d777bea..e01ce500f 100644 --- a/uv.lock +++ b/uv.lock @@ -513,6 +513,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/1e/27b67ee4d10cc755aa9a35d45aa476d7bb2366c4dea91b0db94fa0fe27bd/boto3-1.43.23-py3-none-any.whl", hash = "sha256:8afc058924ef8a5c62467fe2e1e2e0304c22018587a044714da89f9c602ba856", size = 140536, upload-time = "2026-06-04T19:39:34.657Z" }, ] +[[package]] +name = "boto3-stubs-lite" +version = "1.43.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore-stubs" }, + { name = "types-s3transfer" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/7b/1fd2c45f0f62f20e293f6ef1a863c7da63cef4572fa7417cc22512bf427b/boto3_stubs_lite-1.43.21.tar.gz", hash = "sha256:c42813b79c5528d6f94fde9e09917c317f2be1ad1e9858b8af57e88baaf28150", size = 73978, upload-time = "2026-06-03T10:08:59.931Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/39/6aeb3e46c9877184c9fd72cc05af56043f57bfe9264caa04be2b3c72b917/boto3_stubs_lite-1.43.21-py3-none-any.whl", hash = "sha256:7106bf04a60d4ec31710683893b88e12796722ee7d059544ff3b39e96f89a88c", size = 43235, upload-time = "2026-06-03T10:08:56.804Z" }, +] + +[package.optional-dependencies] +boto3 = [ + { name = "boto3" }, +] + [[package]] name = "botocore" version = "1.43.23" @@ -527,6 +546,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/0e/63e4720c6fe6dab278faf9f09b59c377e49a9f9a1afaec2c69dc2e7b9de2/botocore-1.43.23-py3-none-any.whl", hash = "sha256:69ff3d951cb644d1d84db646663c7eb919dc9c0c47e5768e947c8a71121b3d77", size = 15147962, upload-time = "2026-06-04T19:39:22.141Z" }, ] +[[package]] +name = "botocore-stubs" +version = "1.43.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-awscrt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/81/79693e833291c00dc89ee610e5e915381b6f08233912e28df50106840780/botocore_stubs-1.43.14.tar.gz", hash = "sha256:9e3bc1fdd51da7473f0df726c82747a1b0ae913449d629659765c247fecc2039", size = 42738, upload-time = "2026-05-25T06:06:37.484Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/ca/f017727b11895908c5dedc829cf2ec35e0c4b2a26ba875db325fef2cefdf/botocore_stubs-1.43.14-py3-none-any.whl", hash = "sha256:fb98f1475c92fd718644e786b5c543a20f1b1f610e89e0a7191c3f1f429c75aa", size = 67093, upload-time = "2026-05-25T06:06:34.532Z" }, +] + [[package]] name = "bracex" version = "2.6" @@ -5496,7 +5527,7 @@ weaviate = [ dev = [ { name = "anyio" }, { name = "boto3-stubs-lite", extra = ["boto3"] }, - { name = "cassandra-driver", marker = "python_full_version < '3.14'" }, + { name = "cassandra-driver" }, { name = "hvac", marker = "python_full_version < '4'" }, { name = "kafka-python-ng" }, { name = "mkdocs" }, @@ -5525,6 +5556,7 @@ dev = [ { name = "sqlalchemy-cockroachdb" }, { name = "tomli" }, { name = "twine" }, + { name = "types-docker" }, { name = "types-paramiko" }, ] docs = [ @@ -5538,9 +5570,11 @@ docs = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] lint = [ + { name = "boto3-stubs-lite", extra = ["boto3"] }, { name = "mypy" }, { name = "pre-commit" }, { name = "ruff" }, + { name = "types-docker" }, { name = "types-paramiko" }, ] test = [ @@ -5624,7 +5658,7 @@ provides-extras = ["arangodb", "aws", "azurite", "cassandra", "clickhouse", "cos dev = [ { name = "anyio", specifier = ">=4" }, { name = "boto3-stubs-lite", extras = ["boto3"] }, - { name = "cassandra-driver", marker = "python_full_version < '3.14'", specifier = ">=3" }, + { name = "cassandra-driver", specifier = ">=3.30" }, { name = "hvac", marker = "python_full_version < '4'", specifier = ">=2" }, { name = "kafka-python-ng", specifier = ">=2" }, { name = "mkdocs", specifier = ">=1.5.3,<2.0.0" }, @@ -5652,6 +5686,7 @@ dev = [ { name = "sqlalchemy-cockroachdb", specifier = ">=2" }, { name = "tomli" }, { name = "twine", specifier = ">=6.2.0" }, + { name = "types-docker", specifier = ">=7.1.0.20260518" }, { name = "types-paramiko", specifier = ">=4" }, ] docs = [ @@ -5664,9 +5699,11 @@ docs = [ { name = "sphinx", marker = "python_full_version >= '3.11'", specifier = ">=9" }, ] lint = [ + { name = "boto3-stubs-lite", extras = ["boto3"] }, { name = "mypy", specifier = ">=1" }, { name = "pre-commit", specifier = ">=4" }, { name = "ruff" }, + { name = "types-docker", specifier = ">=7.1.0.20260518" }, { name = "types-paramiko", specifier = ">=4" }, ] test = [ @@ -5816,6 +5853,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, ] +[[package]] +name = "types-awscrt" +version = "0.33.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/e3/40e8148a07a3c1c6fb151eddcfdd94083560e759e954b1f5a67ca0b36bcf/types_awscrt-0.33.0.tar.gz", hash = "sha256:803bc7e7e2f6172a0abd71df6593368f82fc23127ca15d287f360e9fcbd3a977", size = 19038, upload-time = "2026-05-25T06:56:04.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/a6/704fbf052edf2497d09292e5f9555b400594924f1154d6c86f2173c2fc11/types_awscrt-0.33.0-py3-none-any.whl", hash = "sha256:95adb57388e1cacc6e7e96fb7ddc735e60096a6151930640bdbe496d9400493a", size = 45687, upload-time = "2026-05-25T06:56:02.549Z" }, +] + +[[package]] +name = "types-docker" +version = "7.1.0.20260518" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-paramiko" }, + { name = "types-requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/71/ee4bd2b713f0b3b0cecc48fd13952b244f187c3b68cfa0408360f102fde8/types_docker-7.1.0.20260518.tar.gz", hash = "sha256:d194c4b82a4110fc58c84af05a5d9de6d3e54f94357ff745d01b5dca12c8eaaa", size = 33868, upload-time = "2026-05-18T06:08:19.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/1d/1d174b6f94afe0de50189de0a9dfd4531d2f8170e6dd9ef68ab616aa770b/types_docker-7.1.0.20260518-py3-none-any.whl", hash = "sha256:59893937816bfe40eb2915c84b31c3131107537500287c47d4a10dd7a9d59deb", size = 48183, upload-time = "2026-05-18T06:08:18.89Z" }, +] + [[package]] name = "types-paramiko" version = "4.0.0.20260518" @@ -5828,6 +5888,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/a2/1a54b77758c9c175526bd0448de353f0563e71ba1ddc8bd4ac0c835deafd/types_paramiko-4.0.0.20260518-py3-none-any.whl", hash = "sha256:0ffaf1a6eb796833a49653cba4c7be13af51c8269d75234972d6239763dda270", size = 38791, upload-time = "2026-05-18T06:06:35.771Z" }, ] +[[package]] +name = "types-requests" +version = "2.33.0.20260518" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/01/c5a19253fe1ac159159ddf9a3a07cec8bb5e486ec4d9002ad2821da0e5d2/types_requests-2.33.0.20260518.tar.gz", hash = "sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e", size = 24752, upload-time = "2026-05-18T06:07:37.966Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/bc/b139710a3b6018f7fb2b9508b35c8af564e61bf2bf4fa619d088f3e16f85/types_requests-2.33.0.20260518-py3-none-any.whl", hash = "sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0", size = 21391, upload-time = "2026-05-18T06:07:37.044Z" }, +] + +[[package]] +name = "types-s3transfer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/64/42689150509eb3e6e82b33ee3d89045de1592488842ddf23c56957786d05/types_s3transfer-0.16.0.tar.gz", hash = "sha256:b4636472024c5e2b62278c5b759661efeb52a81851cde5f092f24100b1ecb443", size = 13557, upload-time = "2025-12-08T08:13:09.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/27/e88220fe6274eccd3bdf95d9382918716d312f6f6cef6a46332d1ee2feff/types_s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:1c0cd111ecf6e21437cb410f5cddb631bfb2263b77ad973e79b9c6d0cb24e0ef", size = 19247, upload-time = "2025-12-08T08:13:08.426Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 69ac3202506fe4a74b5e5f233a1b1713f0784604 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 22:51:50 +0200 Subject: [PATCH 20/29] fix(ruff): add **/*_example.py to per-file-ignores for T201 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 87e9ee298..aa7dd18db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -254,6 +254,7 @@ ignore = [ [tool.ruff.lint.per-file-ignores] "**/example_*.py" = ["T201"] +"**/*_example.py" = ["T201"] "**/examples/*.py" = ["T201"] [tool.ruff.lint.pyupgrade] From 865761cea7803dea2cc91cc26da26c027d810da7 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Thu, 4 Jun 2026 01:47:20 +0200 Subject: [PATCH 21/29] fix(aws): wrong path of test --- tests/community/aws/test_aws.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/community/aws/test_aws.py b/tests/community/aws/test_aws.py index c403f5129..296c977b6 100644 --- a/tests/community/aws/test_aws.py +++ b/tests/community/aws/test_aws.py @@ -7,8 +7,9 @@ from testcontainers.core.image import DockerImage from testcontainers.community.aws import AWSLambdaContainer from testcontainers.community.aws.aws_lambda import RIE_PATH +from pathlib import Path -DOCKER_FILE_PATH = "./modules/aws/tests/lambda_sample" +DOCKER_FILE_PATH = Path(__file__).parent / "lambda_sample" IMAGE_TAG = "lambda:test" From a6b2e44acc2df4dccc2c03064ba1e7db77a1e86b Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Fri, 5 Jun 2026 15:49:02 +0200 Subject: [PATCH 22/29] chore(lint): Fix linting errors, re-activate ruff on tests We want autoformat and the https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt checks for tests. If some rules are annyoing, just disable them with per-file-ignores. Also ruff is very conservative in it's fixes. so just keep the defaults --- docs/modules/trino_example.py | 3 +- pyproject.toml | 17 +++++- .../community/cosmosdb/_emulator.py | 4 +- src/testcontainers/core/container.py | 3 +- src/testcontainers/core/docker_client.py | 4 +- src/testcontainers/core/image.py | 6 +- tests/community/arangodb/test_arangodb.py | 3 +- tests/community/aws/test_aws.py | 14 ++--- .../network_container/netowrk_container.py | 3 +- tests/community/azurite/test_azurite.py | 5 +- tests/community/chroma/test_chroma.py | 3 +- .../cosmosdb/test_cosmosdb_emulator.py | 1 - .../cosmosdb/test_cosmosdb_mongodb.py | 1 + .../community/cosmosdb/test_cosmosdb_nosql.py | 1 - tests/community/db2/test_db2.py | 4 +- tests/community/generic/conftest.py | 4 +- .../generic/samples/advance_1/app/main.py | 3 +- tests/community/generic/test_server.py | 7 ++- tests/community/generic/test_sql.py | 7 ++- tests/community/google/test_google.py | 3 +- tests/community/influxdb/test_influxdb.py | 15 +++-- tests/community/kafka/test_kafka.py | 2 +- tests/community/kafka/test_redpanda.py | 6 +- tests/community/keycloak/test_keycloak.py | 1 + tests/community/mailpit/test_mailpit.py | 2 +- tests/community/memcached/test_memcached.py | 4 +- tests/community/milvus/test_milvus.py | 3 +- tests/community/mongodb/test_mongodb.py | 5 +- tests/community/mssql/test_mssql.py | 2 +- tests/community/mysql/test_mysql.py | 6 +- tests/community/nats/test_nats.py | 7 ++- tests/community/nats/test_nats_jetstream.py | 5 +- tests/community/ollama/test_ollama.py | 1 + tests/community/openfga/test_openfga.py | 4 +- tests/community/opensearch/test_opensearch.py | 4 +- tests/community/oracle-free/test_oracle.py | 14 ++--- tests/community/qdrant/test_qdrant.py | 7 ++- tests/community/rabbitmq/test_rabbitmq.py | 10 ++-- tests/community/redis/test_redis.py | 3 +- tests/community/registry/test_registry.py | 2 +- tests/community/selenium/test_selenium.py | 2 +- tests/community/trino/test_trino.py | 3 +- tests/community/vault/test_vault.py | 1 + tests/community/weaviate/test_weaviate.py | 3 +- tests/core/_local_registry_container.py | 2 +- tests/core/conftest.py | 7 ++- tests/core/test_auth.py | 3 +- tests/core/test_compose.py | 14 ++--- tests/core/test_config.py | 57 +++++++++---------- tests/core/test_container.py | 5 +- tests/core/test_core.py | 2 +- tests/core/test_core_ports.py | 6 +- tests/core/test_core_registry.py | 7 ++- tests/core/test_docker_client.py | 16 +++--- tests/core/test_docker_in_docker.py | 18 +++--- tests/core/test_image.py | 16 +++--- tests/core/test_inspect.py | 1 - tests/core/test_labels.py | 7 ++- tests/core/test_network.py | 9 +-- tests/core/test_protocol_compliance.py | 7 +-- tests/core/test_ryuk.py | 12 ++-- tests/core/test_socat.py | 2 +- tests/core/test_transferable.py | 8 +-- tests/core/test_utils.py | 19 ++++--- tests/core/test_wait_strategies.py | 12 ++-- .../core/test_wait_strategies_integration.py | 7 +-- tests/core/test_waiting_utils.py | 2 +- 67 files changed, 232 insertions(+), 215 deletions(-) diff --git a/docs/modules/trino_example.py b/docs/modules/trino_example.py index 3174a8260..b2466ca23 100644 --- a/docs/modules/trino_example.py +++ b/docs/modules/trino_example.py @@ -1,7 +1,8 @@ import trino -from testcontainers.community.trino import TrinoContainer from trino.exceptions import TrinoQueryError +from testcontainers.community.trino import TrinoContainer + def basic_example(): with TrinoContainer() as trino_container: diff --git a/pyproject.toml b/pyproject.toml index aa7dd18db..c01cc96a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -224,8 +224,6 @@ line-length = 120 fix = true [tool.ruff.lint] -fixable = ["I"] -exclude = ["**/tests/**/*.py"] select = [ "YTT", "B", @@ -248,14 +246,24 @@ select = [ ] ignore = [ "E501", - "INP001", "A004", + "PT011",# there are a lof broad exception catches, fix later + "PT012", # multiple statements in pytest.raises are okay + "PT030", # also broad Depecration Warnings are okay + "TC001","TC002","TC003" # performance effect of type-like imports is minimal ] [tool.ruff.lint.per-file-ignores] "**/example_*.py" = ["T201"] "**/*_example.py" = ["T201"] "**/examples/*.py" = ["T201"] +"tests/**" = [ + "SIM", # we don't need simple code in tests + "T201", # prints are okay in tests + "T203",# pprint is okay in tests + "B017",# assert Exception is okay in tests + "RUF059", # unused unpacked variables are okay in tests +] [tool.ruff.lint.pyupgrade] keep-runtime-typing = true @@ -263,6 +271,9 @@ keep-runtime-typing = true [tool.ruff.lint.flake8-type-checking] strict = true +[tool.ruff.lint.flake8-pytest-style] +parametrize-names-type = "csv" + [tool.mypy] python_version = "3.10" namespace_packages = true diff --git a/src/testcontainers/community/cosmosdb/_emulator.py b/src/testcontainers/community/cosmosdb/_emulator.py index ffc79cf73..aed3fda37 100644 --- a/src/testcontainers/community/cosmosdb/_emulator.py +++ b/src/testcontainers/community/cosmosdb/_emulator.py @@ -32,7 +32,9 @@ def __init__( "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest" ), partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None), - enable_data_persistence: bool = os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false").strip().lower() in _ENV_TRUTHY, + enable_data_persistence: bool = ( + os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false").strip().lower() in _ENV_TRUTHY + ), key: str = os.getenv( "AZURE_COSMOS_EMULATOR_KEY", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", diff --git a/src/testcontainers/core/container.py b/src/testcontainers/core/container.py index a4330a5f2..361d1776a 100644 --- a/src/testcontainers/core/container.py +++ b/src/testcontainers/core/container.py @@ -4,11 +4,10 @@ import pathlib import sys import tarfile -from collections.abc import Mapping from os import PathLike from socket import socket from types import TracebackType -from typing import TYPE_CHECKING, Any, Optional, TypedDict, Union, cast +from typing import TYPE_CHECKING, Any, Optional, TypedDict, Union import docker.errors from docker import version diff --git a/src/testcontainers/core/docker_client.py b/src/testcontainers/core/docker_client.py index 780c83790..34086beb5 100644 --- a/src/testcontainers/core/docker_client.py +++ b/src/testcontainers/core/docker_client.py @@ -470,11 +470,11 @@ def login(self, auth_config: DockerAuthInfo) -> None: login_info = self.client.login(**auth_config._asdict()) LOGGER.debug(f"logged in using {login_info}") - def client_networks_create(self, name: str, param: dict[str, Any]) -> "DockerNetwork": + def client_networks_create(self, name: str, param: dict[str, Any]) -> DockerNetwork: labels = create_labels("", param.get("labels")) return self.client.networks.create(name, **{**param, "labels": labels}) - def get_container_inspect_info(self, container_id: str) -> "ContainerInspectInfo": + def get_container_inspect_info(self, container_id: str) -> ContainerInspectInfo: """Get container inspect information with fresh data.""" container = self.client.containers.get(container_id) return ContainerInspectInfo.from_dict(container.attrs) diff --git a/src/testcontainers/core/image.py b/src/testcontainers/core/image.py index 4654694ab..9a39ec1f3 100644 --- a/src/testcontainers/core/image.py +++ b/src/testcontainers/core/image.py @@ -1,8 +1,7 @@ from __future__ import annotations from os import PathLike -from types import TracebackType -from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Optional, Union import docker.errors from typing_extensions import Self @@ -11,7 +10,8 @@ from testcontainers.core.utils import setup_logger if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Iterator + from types import TracebackType from docker._types import JSON # only exists in docker-stubs, not at runtime from docker.models.images import Image diff --git a/tests/community/arangodb/test_arangodb.py b/tests/community/arangodb/test_arangodb.py index 00ac2b927..fc191c48d 100644 --- a/tests/community/arangodb/test_arangodb.py +++ b/tests/community/arangodb/test_arangodb.py @@ -2,12 +2,13 @@ ArangoDB Container Tests """ +import platform + import pytest from arango import ArangoClient from arango.exceptions import DatabaseCreateError, ServerVersionError from testcontainers.community.arangodb import ArangoDbContainer -import platform ARANGODB_IMAGE_NAME = "arangodb" IMAGE_VERSION = "3.11.8" diff --git a/tests/community/aws/test_aws.py b/tests/community/aws/test_aws.py index 296c977b6..4466f642a 100644 --- a/tests/community/aws/test_aws.py +++ b/tests/community/aws/test_aws.py @@ -1,13 +1,13 @@ -import re import os +import re +from pathlib import Path +from unittest.mock import patch import pytest -from unittest.mock import patch -from testcontainers.core.image import DockerImage from testcontainers.community.aws import AWSLambdaContainer from testcontainers.community.aws.aws_lambda import RIE_PATH -from pathlib import Path +from testcontainers.core.image import DockerImage DOCKER_FILE_PATH = Path(__file__).parent / "lambda_sample" IMAGE_TAG = "lambda:test" @@ -30,12 +30,12 @@ def test_aws_lambda_container(): def test_aws_lambda_container_external_env_vars(): - vars = { + variables = { "AWS_DEFAULT_REGION": "region", "AWS_ACCESS_KEY_ID": "id", "AWS_SECRET_ACCESS_KEY": "key", } - with patch.dict(os.environ, vars): + with patch.dict(os.environ, variables): with DockerImage(path=DOCKER_FILE_PATH, tag="test-lambda-env-vars:latest") as image: with AWSLambdaContainer(image=image, port=8080) as func: assert func.env["AWS_DEFAULT_REGION"] == "region" @@ -52,6 +52,6 @@ def test_aws_lambda_container_no_port(): def test_aws_lambda_container_no_path(): with pytest.raises(TypeError): - with DockerImage(path=DOCKER_FILE_PATH, tag="test-lambda-no-path:latest") as image: + with DockerImage(path=DOCKER_FILE_PATH, tag="test-lambda-no-path:latest"): with AWSLambdaContainer() as func: # noqa: F841 pass diff --git a/tests/community/azurite/samples/network_container/netowrk_container.py b/tests/community/azurite/samples/network_container/netowrk_container.py index 4831d4089..76b420d4b 100644 --- a/tests/community/azurite/samples/network_container/netowrk_container.py +++ b/tests/community/azurite/samples/network_container/netowrk_container.py @@ -1,6 +1,7 @@ -from azure.storage.blob import BlobClient, BlobServiceClient import os +from azure.storage.blob import BlobServiceClient + def hello_from_external_container(): """ diff --git a/tests/community/azurite/test_azurite.py b/tests/community/azurite/test_azurite.py index 03329adac..f1dd305c2 100644 --- a/tests/community/azurite/test_azurite.py +++ b/tests/community/azurite/test_azurite.py @@ -1,17 +1,14 @@ import logging -import time from pathlib import Path from azure.storage.blob import BlobServiceClient from testcontainers.community.azurite import AzuriteContainer, ConnectionStringType - -from testcontainers.core.image import DockerImage from testcontainers.core.container import DockerContainer +from testcontainers.core.image import DockerImage from testcontainers.core.network import Network from testcontainers.core.waiting_utils import wait_for_logs - logger = logging.getLogger(__name__) diff --git a/tests/community/chroma/test_chroma.py b/tests/community/chroma/test_chroma.py index 26dc2dd0c..ef2c95410 100644 --- a/tests/community/chroma/test_chroma.py +++ b/tests/community/chroma/test_chroma.py @@ -1,6 +1,7 @@ -from testcontainers.community.chroma import ChromaContainer import chromadb +from testcontainers.community.chroma import ChromaContainer + def test_docker_run_chroma(): with ChromaContainer(image="chromadb/chroma:1.0.0") as chroma: diff --git a/tests/community/cosmosdb/test_cosmosdb_emulator.py b/tests/community/cosmosdb/test_cosmosdb_emulator.py index eb951d452..62363a8f2 100644 --- a/tests/community/cosmosdb/test_cosmosdb_emulator.py +++ b/tests/community/cosmosdb/test_cosmosdb_emulator.py @@ -1,4 +1,3 @@ -import pytest from testcontainers.community.cosmosdb._emulator import CosmosDBEmulatorContainer diff --git a/tests/community/cosmosdb/test_cosmosdb_mongodb.py b/tests/community/cosmosdb/test_cosmosdb_mongodb.py index 2ddd4cac7..4b07d43a9 100644 --- a/tests/community/cosmosdb/test_cosmosdb_mongodb.py +++ b/tests/community/cosmosdb/test_cosmosdb_mongodb.py @@ -1,4 +1,5 @@ import pytest + from testcontainers.community.cosmosdb import CosmosDBMongoEndpointContainer diff --git a/tests/community/cosmosdb/test_cosmosdb_nosql.py b/tests/community/cosmosdb/test_cosmosdb_nosql.py index c7e418919..20684c9cd 100644 --- a/tests/community/cosmosdb/test_cosmosdb_nosql.py +++ b/tests/community/cosmosdb/test_cosmosdb_nosql.py @@ -1,4 +1,3 @@ -import pytest from testcontainers.community.cosmosdb import CosmosDBNoSQLEndpointContainer diff --git a/tests/community/db2/test_db2.py b/tests/community/db2/test_db2.py index 1484814e9..d5068b2d4 100644 --- a/tests/community/db2/test_db2.py +++ b/tests/community/db2/test_db2.py @@ -1,10 +1,8 @@ -from unittest import mock - import pytest import sqlalchemy -from testcontainers.core.utils import is_arm from testcontainers.community.db2 import Db2Container +from testcontainers.core.utils import is_arm pytestmark = pytest.mark.xdist_group("db2") diff --git a/tests/community/generic/conftest.py b/tests/community/generic/conftest.py index 5aa0b7833..810ab426e 100644 --- a/tests/community/generic/conftest.py +++ b/tests/community/generic/conftest.py @@ -1,5 +1,7 @@ -import pytest from typing import Callable + +import pytest + from testcontainers.core.container import DockerClient diff --git a/tests/community/generic/samples/advance_1/app/main.py b/tests/community/generic/samples/advance_1/app/main.py index 27cc6bbec..dd69b6436 100644 --- a/tests/community/generic/samples/advance_1/app/main.py +++ b/tests/community/generic/samples/advance_1/app/main.py @@ -1,11 +1,10 @@ # This app will use redis to store given key-value pairs. import os -import redis +import redis from fastapi import FastAPI - app = FastAPI() redis_host = os.getenv("REDIS_HOST") diff --git a/tests/community/generic/test_server.py b/tests/community/generic/test_server.py index 66f0073e2..4657b4f00 100644 --- a/tests/community/generic/test_server.py +++ b/tests/community/generic/test_server.py @@ -5,16 +5,17 @@ import pytest from httpx import get -from testcontainers.core.waiting_utils import wait_for_logs -from testcontainers.core.image import DockerImage from testcontainers.community.generic import ServerContainer +from testcontainers.core.image import DockerImage +from testcontainers.core.waiting_utils import wait_for_logs TEST_DIR = Path(__file__).parent @pytest.mark.parametrize("test_image_cleanup", [True, False]) @pytest.mark.parametrize("test_image_tag", [None, "custom-image:test"]) -def test_server_container(test_image_tag: Optional[str], test_image_cleanup: bool, check_for_image, port=9000): +def test_server_container(test_image_tag: Optional[str], test_image_cleanup: bool, check_for_image): + port = 9000 with ( DockerImage( path=TEST_DIR / "samples/python_server", diff --git a/tests/community/generic/test_sql.py b/tests/community/generic/test_sql.py index 7aade4952..5db3010e9 100644 --- a/tests/community/generic/test_sql.py +++ b/tests/community/generic/test_sql.py @@ -1,9 +1,10 @@ -import pytest from unittest.mock import patch -from testcontainers.core.exceptions import ContainerStartException -from testcontainers.community.generic.sql import SqlContainer +import pytest + from testcontainers.community.generic.providers.sql_connection_wait_strategy import SqlAlchemyConnectWaitStrategy +from testcontainers.community.generic.sql import SqlContainer +from testcontainers.core.exceptions import ContainerStartException class SimpleSqlContainer(SqlContainer): diff --git a/tests/community/google/test_google.py b/tests/community/google/test_google.py index cdf890b80..a4c43cb8b 100644 --- a/tests/community/google/test_google.py +++ b/tests/community/google/test_google.py @@ -1,8 +1,9 @@ from queue import Queue + from google.cloud.datastore import Entity +from testcontainers.community.google import DatastoreContainer, PubSubContainer from testcontainers.core.waiting_utils import wait_for_logs -from testcontainers.community.google import PubSubContainer, DatastoreContainer def test_pubsub_container(): diff --git a/tests/community/influxdb/test_influxdb.py b/tests/community/influxdb/test_influxdb.py index d628939b6..357d23df3 100644 --- a/tests/community/influxdb/test_influxdb.py +++ b/tests/community/influxdb/test_influxdb.py @@ -12,33 +12,32 @@ # under the License. from datetime import datetime -from typing import Type +import pytest from influxdb.resultset import ResultSet from influxdb_client import Bucket from influxdb_client.client.write_api import SYNCHRONOUS -from pytest import mark from testcontainers.community.influxdb import InfluxDbContainer from testcontainers.community.influxdb1 import InfluxDb1Container from testcontainers.community.influxdb2 import InfluxDb2Container -@mark.parametrize( - ["image", "influxdb_container_class", "exposed_port"], +@pytest.mark.parametrize( + "image, influxdb_container_class, exposed_port", [ ("influxdb:2.7", InfluxDb1Container, 8086), ("influxdb:1.8", InfluxDb2Container, 8086), ], ) -def test_influxdbcontainer_get_url(image: str, influxdb_container_class: Type[InfluxDbContainer], exposed_port: int): +def test_influxdbcontainer_get_url(image: str, influxdb_container_class: type[InfluxDbContainer], exposed_port: int): with influxdb_container_class(image, host_port=exposed_port) as influxdb_container: connection_url = influxdb_container.get_url() assert str(exposed_port) in connection_url -@mark.parametrize( - ["image", "influxdb_container_class", "expected_version"], +@pytest.mark.parametrize( + "image, influxdb_container_class, expected_version", [ ("influxdb:1.8", InfluxDb1Container, "1.8.10"), ("influxdb:1.8.10", InfluxDb1Container, "1.8.10"), @@ -47,7 +46,7 @@ def test_influxdbcontainer_get_url(image: str, influxdb_container_class: Type[In ], ) def test_influxdbcontainer_get_influxdb_version( - image: str, influxdb_container_class: Type[InfluxDbContainer], expected_version: str + image: str, influxdb_container_class: type[InfluxDbContainer], expected_version: str ): with influxdb_container_class(image) as influxdb_container: assert influxdb_container.get_influxdb_version().startswith(expected_version) diff --git a/tests/community/kafka/test_kafka.py b/tests/community/kafka/test_kafka.py index a9651e162..4bec6950f 100644 --- a/tests/community/kafka/test_kafka.py +++ b/tests/community/kafka/test_kafka.py @@ -1,8 +1,8 @@ import pytest from kafka import KafkaAdminClient, KafkaConsumer, KafkaProducer, TopicPartition -from testcontainers.core.network import Network from testcontainers.community.kafka import KafkaContainer, kafka_config +from testcontainers.core.network import Network def test_kafka_producer_consumer(): diff --git a/tests/community/kafka/test_redpanda.py b/tests/community/kafka/test_redpanda.py index e92497c82..385f98713 100644 --- a/tests/community/kafka/test_redpanda.py +++ b/tests/community/kafka/test_redpanda.py @@ -1,9 +1,9 @@ -import pytest -from requests import post, get from json import dumps -from kafka import KafkaConsumer, KafkaProducer, TopicPartition, KafkaAdminClient +import pytest +from kafka import KafkaAdminClient, KafkaConsumer, KafkaProducer, TopicPartition from kafka.admin import NewTopic +from requests import get, post from testcontainers.community.kafka import RedpandaContainer diff --git a/tests/community/keycloak/test_keycloak.py b/tests/community/keycloak/test_keycloak.py index 1880be14e..5aaf44ac5 100644 --- a/tests/community/keycloak/test_keycloak.py +++ b/tests/community/keycloak/test_keycloak.py @@ -1,4 +1,5 @@ import pytest + from testcontainers.community.keycloak import KeycloakContainer diff --git a/tests/community/mailpit/test_mailpit.py b/tests/community/mailpit/test_mailpit.py index 0818165dc..88e82f3ae 100644 --- a/tests/community/mailpit/test_mailpit.py +++ b/tests/community/mailpit/test_mailpit.py @@ -1,6 +1,6 @@ import smtplib -from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText import pytest diff --git a/tests/community/memcached/test_memcached.py b/tests/community/memcached/test_memcached.py index d7e82092a..4bcb60f55 100644 --- a/tests/community/memcached/test_memcached.py +++ b/tests/community/memcached/test_memcached.py @@ -1,9 +1,9 @@ import socket -from testcontainers.community.memcached import MemcachedContainer - import pytest +from testcontainers.community.memcached import MemcachedContainer + def test_memcached_host_and_exposed_port(): with MemcachedContainer("memcached:1.6-alpine") as memcached: diff --git a/tests/community/milvus/test_milvus.py b/tests/community/milvus/test_milvus.py index 495f49535..3d045338d 100644 --- a/tests/community/milvus/test_milvus.py +++ b/tests/community/milvus/test_milvus.py @@ -21,7 +21,8 @@ def test_run_milvus_success(version: str): exposed_port = milvus_container.get_exposed_port(milvus_container.port) url = milvus_container.get_connection_url() - assert url and str(exposed_port) in url + assert url + assert str(exposed_port) in url @pytest.mark.parametrize("version", VERSIONS) diff --git a/tests/community/mongodb/test_mongodb.py b/tests/community/mongodb/test_mongodb.py index 284f221d2..e4cc9af9e 100644 --- a/tests/community/mongodb/test_mongodb.py +++ b/tests/community/mongodb/test_mongodb.py @@ -1,9 +1,8 @@ import time + import pytest -from pymongo import MongoClient -from pymongo.errors import OperationFailure -from testcontainers.community.mongodb import MongoDbContainer, MongoDBAtlasLocalContainer +from testcontainers.community.mongodb import MongoDBAtlasLocalContainer, MongoDbContainer @pytest.mark.parametrize("version", ["7.0.7", "6.0.14", "5.0.26"]) diff --git a/tests/community/mssql/test_mssql.py b/tests/community/mssql/test_mssql.py index 22a282ece..2ffd9b2b2 100644 --- a/tests/community/mssql/test_mssql.py +++ b/tests/community/mssql/test_mssql.py @@ -1,8 +1,8 @@ import pytest import sqlalchemy -from testcontainers.core.utils import is_arm from testcontainers.community.mssql import SqlServerContainer +from testcontainers.core.utils import is_arm pytestmark = pytest.mark.xdist_group("mssql") diff --git a/tests/community/mysql/test_mysql.py b/tests/community/mysql/test_mysql.py index 23b66f6c4..61086c981 100644 --- a/tests/community/mysql/test_mysql.py +++ b/tests/community/mysql/test_mysql.py @@ -1,12 +1,12 @@ -from pathlib import Path import re +from pathlib import Path from unittest import mock import pytest import sqlalchemy -from testcontainers.core.utils import is_arm from testcontainers.community.mysql import MySqlContainer +from testcontainers.core.utils import is_arm @pytest.mark.inside_docker_check @@ -77,7 +77,7 @@ def test_docker_env_variables(): ], ) def test_mysql_dialect_expecting_error_on_mysql_prefix(dialect: str): - match = f"Please remove *.* prefix from dialect parameter" + match = "Please remove *.* prefix from dialect parameter" with pytest.raises(ValueError, match=match): _ = MySqlContainer("mariadb:10.6.5", dialect=dialect) diff --git a/tests/community/nats/test_nats.py b/tests/community/nats/test_nats.py index 25236e2a8..624c2b0e2 100644 --- a/tests/community/nats/test_nats.py +++ b/tests/community/nats/test_nats.py @@ -1,10 +1,11 @@ -from testcontainers.community.nats import NatsContainer from uuid import uuid4 -import pytest +import pytest from nats import connect as nats_connect from nats.aio.client import Client as NATSClient +from testcontainers.community.nats import NatsContainer + async def get_client(container: NatsContainer) -> "NATSClient": """ @@ -22,7 +23,7 @@ def test_basic_container_ops(): with NatsContainer() as container: # Not sure how to get type information without doing this container: NatsContainer = container - h, p = container.nats_host_and_port() + h, _p = container.nats_host_and_port() assert h == "localhost" uri = container.nats_uri() management_uri = container.nats_management_uri() diff --git a/tests/community/nats/test_nats_jetstream.py b/tests/community/nats/test_nats_jetstream.py index 31e488c51..f9a00e101 100644 --- a/tests/community/nats/test_nats_jetstream.py +++ b/tests/community/nats/test_nats_jetstream.py @@ -1,10 +1,11 @@ -from testcontainers.community.nats import NatsContainer from uuid import uuid4 -import pytest +import pytest from nats import connect as nats_connect from nats.aio.client import Client as NATSClient +from testcontainers.community.nats import NatsContainer + async def get_client(container: NatsContainer) -> "NATSClient": """ diff --git a/tests/community/ollama/test_ollama.py b/tests/community/ollama/test_ollama.py index 79ba1c769..99836d139 100644 --- a/tests/community/ollama/test_ollama.py +++ b/tests/community/ollama/test_ollama.py @@ -3,6 +3,7 @@ from pathlib import Path import requests + from testcontainers.community.ollama import OllamaContainer diff --git a/tests/community/openfga/test_openfga.py b/tests/community/openfga/test_openfga.py index 3e1ec5a36..031c14f49 100644 --- a/tests/community/openfga/test_openfga.py +++ b/tests/community/openfga/test_openfga.py @@ -1,6 +1,8 @@ +from sys import version_info + import pytest + from testcontainers.community.openfga import OpenFGAContainer -from sys import version_info def test_openfga(): diff --git a/tests/community/opensearch/test_opensearch.py b/tests/community/opensearch/test_opensearch.py index 23a7f9828..b5e140064 100644 --- a/tests/community/opensearch/test_opensearch.py +++ b/tests/community/opensearch/test_opensearch.py @@ -1,7 +1,7 @@ -from testcontainers.community.opensearch import OpenSearchContainer - import pytest +from testcontainers.community.opensearch import OpenSearchContainer + @pytest.fixture(autouse=True) def disable_logging(): diff --git a/tests/community/oracle-free/test_oracle.py b/tests/community/oracle-free/test_oracle.py index 1effbe89f..5323f3c25 100644 --- a/tests/community/oracle-free/test_oracle.py +++ b/tests/community/oracle-free/test_oracle.py @@ -1,8 +1,8 @@ import pytest import sqlalchemy -from testcontainers.core.utils import is_arm from testcontainers.community.oracle import OracleDbContainer +from testcontainers.core.utils import is_arm @pytest.mark.skipif(is_arm(), reason="oracle-free container not available for ARM") @@ -11,7 +11,7 @@ def test_docker_run_oracle_with_system_password(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val @@ -22,7 +22,7 @@ def test_docker_run_oracle_with_username_password(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val @@ -33,7 +33,7 @@ def test_docker_run_oracle_with_custom_db_and_system_username_password(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val @@ -44,7 +44,7 @@ def test_docker_run_oracle_with_custom_db_and_app_username_password(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val @@ -55,7 +55,7 @@ def test_docker_run_oracle_with_default_db_and_app_username_password(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val @@ -66,7 +66,7 @@ def test_docker_run_oracle_with_cdb_and_system_username(): engine = sqlalchemy.create_engine(oracledb.get_connection_url()) with engine.begin() as connection: test_val = 1 - result = connection.execute(sqlalchemy.text("SELECT {} FROM dual".format(test_val))) + result = connection.execute(sqlalchemy.text(f"SELECT {test_val} FROM dual")) for row in result: assert row[0] == test_val diff --git a/tests/community/qdrant/test_qdrant.py b/tests/community/qdrant/test_qdrant.py index 82b9aaa68..61fabbad1 100644 --- a/tests/community/qdrant/test_qdrant.py +++ b/tests/community/qdrant/test_qdrant.py @@ -1,10 +1,11 @@ -import pytest -from testcontainers.community.qdrant import QdrantContainer import uuid -from grpc import RpcError from pathlib import Path +import pytest import qdrant_client +from grpc import RpcError + +from testcontainers.community.qdrant import QdrantContainer def test_docker_run_qdrant(): diff --git a/tests/community/rabbitmq/test_rabbitmq.py b/tests/community/rabbitmq/test_rabbitmq.py index 71366ce35..8b283588e 100644 --- a/tests/community/rabbitmq/test_rabbitmq.py +++ b/tests/community/rabbitmq/test_rabbitmq.py @@ -13,12 +13,12 @@ @pytest.mark.parametrize( - argnames=["port", "username", "password", "vhost"], + argnames="port, username, password, vhost", argvalues=[ - [None, None, None, None], # use the defaults - [5673, None, None, None], # test with custom port - [None, "my_test_user", "my_secret_password", None], # test with custom credentials - [None, None, None, "vhost"], # test with custom vhost + (None, None, None, None), # use the defaults + (5673, None, None, None), # test with custom port + (None, "my_test_user", "my_secret_password", None), # test with custom credentials + (None, None, None, "vhost"), # test with custom vhost ], ) def test_docker_run_rabbitmq( diff --git a/tests/community/redis/test_redis.py b/tests/community/redis/test_redis.py index 08b89455a..5185aa88d 100644 --- a/tests/community/redis/test_redis.py +++ b/tests/community/redis/test_redis.py @@ -1,9 +1,10 @@ import time -from testcontainers.community.redis import RedisContainer, AsyncRedisContainer import pytest import redis +from testcontainers.community.redis import AsyncRedisContainer, RedisContainer + def test_docker_run_redis(): config = RedisContainer() diff --git a/tests/community/registry/test_registry.py b/tests/community/registry/test_registry.py index c7d4f6c9e..355bcce24 100644 --- a/tests/community/registry/test_registry.py +++ b/tests/community/registry/test_registry.py @@ -1,7 +1,7 @@ from requests import Response, get from requests.auth import HTTPBasicAuth -from testcontainers.community.registry import DockerRegistryContainer +from testcontainers.community.registry import DockerRegistryContainer REGISTRY_USERNAME: str = "foo" REGISTRY_PASSWORD: str = "bar" diff --git a/tests/community/selenium/test_selenium.py b/tests/community/selenium/test_selenium.py index 190b5d2e4..9d0ad0b2b 100644 --- a/tests/community/selenium/test_selenium.py +++ b/tests/community/selenium/test_selenium.py @@ -6,8 +6,8 @@ from selenium.webdriver import DesiredCapabilities from selenium.webdriver.common.by import By -from testcontainers.core.utils import is_arm from testcontainers.community.selenium import BrowserWebDriverContainer +from testcontainers.core.utils import is_arm @pytest.mark.parametrize("caps", [DesiredCapabilities.CHROME, DesiredCapabilities.FIREFOX]) diff --git a/tests/community/trino/test_trino.py b/tests/community/trino/test_trino.py index 5f6fc4821..0d593fc58 100644 --- a/tests/community/trino/test_trino.py +++ b/tests/community/trino/test_trino.py @@ -1,6 +1,7 @@ -from testcontainers.community.trino import TrinoContainer from trino.dbapi import connect +from testcontainers.community.trino import TrinoContainer + def test_docker_run_trino(): container = TrinoContainer("trinodb/trino:451") diff --git a/tests/community/vault/test_vault.py b/tests/community/vault/test_vault.py index 4c6b6b605..c1006536d 100644 --- a/tests/community/vault/test_vault.py +++ b/tests/community/vault/test_vault.py @@ -1,4 +1,5 @@ import hvac + from testcontainers.community.vault import VaultContainer diff --git a/tests/community/weaviate/test_weaviate.py b/tests/community/weaviate/test_weaviate.py index e10ac4f97..d91e9da92 100644 --- a/tests/community/weaviate/test_weaviate.py +++ b/tests/community/weaviate/test_weaviate.py @@ -1,6 +1,7 @@ -from testcontainers.community.weaviate import WeaviateContainer import weaviate +from testcontainers.community.weaviate import WeaviateContainer + def test_docker_run_weaviate(): with WeaviateContainer() as container: diff --git a/tests/core/_local_registry_container.py b/tests/core/_local_registry_container.py index 2687943f5..c547ee58c 100644 --- a/tests/core/_local_registry_container.py +++ b/tests/core/_local_registry_container.py @@ -81,7 +81,7 @@ def _readiness_probe(self) -> None: response: Response = get(url, timeout=1) response.raise_for_status() - def start(self) -> "_LocalRegistryContainer": + def start(self) -> _LocalRegistryContainer: if self.username and self.password: self.with_env("REGISTRY_AUTH_HTPASSWD_REALM", "local-registry") self.with_env("REGISTRY_AUTH_HTPASSWD_PATH", self.credentials_path) diff --git a/tests/core/conftest.py b/tests/core/conftest.py index 0ba178a5b..4f0f77a7e 100644 --- a/tests/core/conftest.py +++ b/tests/core/conftest.py @@ -1,10 +1,11 @@ +import sys from pathlib import Path +from pprint import pprint +from typing import Callable import pytest -from typing import Callable + from testcontainers.core.docker_client import DockerClient -from pprint import pprint -import sys PROJECT_DIR = Path(__file__).parent.parent.parent.resolve() diff --git a/tests/core/test_auth.py b/tests/core/test_auth.py index 0e1639811..f518fff29 100644 --- a/tests/core/test_auth.py +++ b/tests/core/test_auth.py @@ -1,7 +1,8 @@ import json + import pytest -from testcontainers.core.auth import parse_docker_auth_config, DockerAuthInfo +from testcontainers.core.auth import DockerAuthInfo, parse_docker_auth_config def test_parse_docker_auth_config_encoded(): diff --git a/tests/core/test_compose.py b/tests/core/test_compose.py index 552f63498..5f9fcf435 100644 --- a/tests/core/test_compose.py +++ b/tests/core/test_compose.py @@ -3,13 +3,13 @@ from pathlib import Path from re import split from time import sleep -from typing import Union, Optional -from urllib.request import urlopen, Request +from typing import Optional, Union +from urllib.request import Request, urlopen import pytest from pytest_mock import MockerFixture -from testcontainers.compose import DockerCompose, ComposeContainer +from testcontainers.compose import ComposeContainer, DockerCompose from testcontainers.core.docker_client import is_podman from testcontainers.core.exceptions import ContainerIsNotRunning, NoSuchPortExposed @@ -169,7 +169,7 @@ def test_compose_volumes(caplog): # execute another time to confirm the file is still there, but we're not keeping the volumes this time volumes.keep_volumes = False with volumes: - stdout, stderr, exitcode = volumes.exec_in_container(["cat", _file_in_volume], "alpine") + stdout, _stderr, exitcode = volumes.exec_in_container(["cat", _file_in_volume], "alpine") assert exitcode == 0 assert "hello" in stdout @@ -228,7 +228,7 @@ def test_compose_multiple_containers_and_ports(): try: # this fails when ipv6 is enabled and docker is forwarding for both 4 + 6 multiple.get_container(service_name="alpine").get_publisher(by_port=81, prefer_ip_version="IPv6") - except: # noqa + except: # noqa: E722 pass ports = [ @@ -369,7 +369,7 @@ def fetch(req: Union[Request, str]): @pytest.mark.parametrize( - argnames=["profiles", "running", "not_running"], + argnames="profiles, running, not_running", argvalues=[ pytest.param(None, ["runs-always"], ["runs-profile-a", "runs-profile-b"], id="default"), pytest.param( @@ -489,7 +489,7 @@ def test_container_info_network_details(): if network_settings.Networks: # Test first network - network_name, network = next(iter(network_settings.Networks.items())) + _network_name, network = next(iter(network_settings.Networks.items())) assert network.IPAddress is not None assert network.Gateway is not None assert network.NetworkID is not None diff --git a/tests/core/test_config.py b/tests/core/test_config.py index ed5d6aa25..da3e2495b 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,22 +1,21 @@ +import logging +import tempfile +from unittest.mock import Mock + import pytest from testcontainers.core.config import ( - TestcontainersConfiguration as TCC, TC_FILE, TestcontainersConfiguration, - get_user_overwritten_connection_mode, - ConnectionMode, get_docker_socket, + get_user_overwritten_connection_mode, +) +from testcontainers.core.config import ( + TestcontainersConfiguration as TCC, ) - -from pytest import MonkeyPatch, mark, LogCaptureFixture - -import logging -import tempfile -from unittest.mock import Mock -def test_read_tc_properties(monkeypatch: MonkeyPatch) -> None: +def test_read_tc_properties(monkeypatch: pytest.MonkeyPatch) -> None: with tempfile.TemporaryDirectory() as tmpdirname: file = f"{tmpdirname}/{TC_FILE}" with open(file, "w") as f: @@ -28,7 +27,7 @@ def test_read_tc_properties(monkeypatch: MonkeyPatch) -> None: assert config.tc_properties == {"tc.host": "some_value"} -def test_hub_image_name_prefix(monkeypatch: MonkeyPatch) -> None: +def test_hub_image_name_prefix(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure that the hub_image_name_prefix configuration variable can be read from the environment """ @@ -37,7 +36,7 @@ def test_hub_image_name_prefix(monkeypatch: MonkeyPatch) -> None: assert config.hub_image_name_prefix == "myregistry.local/" -def test_set_tc_properties(monkeypatch: MonkeyPatch) -> None: +def test_set_tc_properties(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure the configuration file variables can be read if no environment variable is set """ @@ -51,11 +50,11 @@ def test_set_tc_properties(monkeypatch: MonkeyPatch) -> None: config = TCC() - assert config.ryuk_disabled == True - assert config.ryuk_privileged == False + assert config.ryuk_disabled + assert not config.ryuk_privileged -def test_override_tc_properties_1(monkeypatch: MonkeyPatch) -> None: +def test_override_tc_properties_1(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure that we can re-set the configuration variables programattically to override testcontainers.properties @@ -72,11 +71,11 @@ def test_override_tc_properties_1(monkeypatch: MonkeyPatch) -> None: config.ryuk_disabled = False config.ryuk_privileged = True - assert config.ryuk_disabled == False - assert config.ryuk_privileged == True + assert not config.ryuk_disabled + assert config.ryuk_privileged -def test_override_tc_properties_2(monkeypatch: MonkeyPatch) -> None: +def test_override_tc_properties_2(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure that we can override the testcontainers.properties with environment variables """ @@ -95,16 +94,16 @@ def test_override_tc_properties_2(monkeypatch: MonkeyPatch) -> None: config = TCC() - assert config.ryuk_disabled == False - assert config.ryuk_privileged == True + assert config.ryuk_disabled is False + assert config.ryuk_privileged is True -@mark.parametrize("docker_auth_config_env", ["key=value", ""]) -@mark.parametrize("warning_dict", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}]) -@mark.parametrize("warning_dict_post", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}]) +@pytest.mark.parametrize("docker_auth_config_env", ["key=value", ""]) +@pytest.mark.parametrize("warning_dict", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}]) +@pytest.mark.parametrize("warning_dict_post", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}]) def test_docker_auth_config( - caplog: LogCaptureFixture, - monkeypatch: MonkeyPatch, + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, docker_auth_config_env: str, warning_dict: dict[str, str], warning_dict_post: dict[str, str], @@ -145,11 +144,11 @@ def test_timeout() -> None: def test_invalid_connection_mode(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("TESTCONTAINERS_CONNECTION_MODE", "FOOBAR") - with pytest.raises(ValueError, match="Error parsing TESTCONTAINERS_CONNECTION_MODE.*FOOBAR.*"): + with pytest.raises(ValueError, match=r"Error parsing TESTCONTAINERS_CONNECTION_MODE.*FOOBAR.*"): get_user_overwritten_connection_mode() -@pytest.mark.parametrize("mode, use_mapped", (("bridge_ip", False), ("gateway_ip", True), ("docker_host", True))) +@pytest.mark.parametrize("mode, use_mapped", [("bridge_ip", False), ("gateway_ip", True), ("docker_host", True)]) def test_valid_connection_mode(monkeypatch: pytest.MonkeyPatch, mode: str, use_mapped: bool) -> None: monkeypatch.setenv("TESTCONTAINERS_CONNECTION_MODE", mode) uo_cmo = get_user_overwritten_connection_mode() @@ -178,8 +177,8 @@ def mock_docker_client_connections(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure the docker client does not make any actual network calls """ - from docker.transport.sshconn import SSHHTTPAdapter from docker.api.client import APIClient + from docker.transport.sshconn import SSHHTTPAdapter # ensure that no actual connection is tried monkeypatch.setattr(SSHHTTPAdapter, "_connect", Mock()) @@ -240,4 +239,4 @@ def test_attribut_error() -> None: from testcontainers.core import config with pytest.raises(AttributeError): - config.missing + config.missing # noqa: B018 diff --git a/tests/core/test_container.py b/tests/core/test_container.py index 3b08779a6..accdc18cf 100644 --- a/tests/core/test_container.py +++ b/tests/core/test_container.py @@ -2,10 +2,9 @@ import pytest +from testcontainers.core.config import ConnectionMode, testcontainers_config from testcontainers.core.container import DockerContainer from testcontainers.core.docker_client import DockerClient -from testcontainers.core.config import ConnectionMode -from testcontainers.core.config import testcontainers_config FAKE_ID = "ABC123" @@ -162,7 +161,7 @@ def test_container_info_network_details(): assert network_settings is not None if network_settings.Networks: - network_name, network = next(iter(network_settings.Networks.items())) + _network_name, network = next(iter(network_settings.Networks.items())) assert network.IPAddress is not None assert network.Gateway is not None assert network.NetworkID is not None diff --git a/tests/core/test_core.py b/tests/core/test_core.py index 3ae86ddf2..4c0d5f2aa 100644 --- a/tests/core/test_core.py +++ b/tests/core/test_core.py @@ -19,7 +19,7 @@ def test_get_logs(): stdout, stderr = container.get_logs() assert isinstance(stdout, bytes) assert isinstance(stderr, bytes) - assert "Hello from Docker".encode() in stdout, "There should be something on stdout" + assert b"Hello from Docker" in stdout, "There should be something on stdout" def test_docker_container_with_env_file(): diff --git a/tests/core/test_core_ports.py b/tests/core/test_core_ports.py index b72075d7d..1738d14a2 100644 --- a/tests/core/test_core_ports.py +++ b/tests/core/test_core_ports.py @@ -1,9 +1,9 @@ -import pytest -from typing import Any, Union, Optional -from testcontainers.core.container import DockerContainer +from typing import Any, Optional, Union +import pytest from docker.errors import APIError +from testcontainers.core.container import DockerContainer from testcontainers.core.docker_client import is_podman diff --git a/tests/core/test_core_registry.py b/tests/core/test_core_registry.py index a6c801b2b..45807e02f 100644 --- a/tests/core/test_core_registry.py +++ b/tests/core/test_core_registry.py @@ -12,7 +12,6 @@ import json import pytest -from ._local_registry_container import _LocalRegistryContainer from docker.errors import NotFound from testcontainers.core.config import testcontainers_config @@ -21,6 +20,8 @@ from testcontainers.core.utils import is_mac from testcontainers.core.waiting_utils import wait_for_logs +from ._local_registry_container import _LocalRegistryContainer + _skip_insecure_registry = pytest.mark.skipif( is_mac() or is_podman() or is_ssh_docker_host(), reason="Insecure HTTP registries are not supported without daemon reconfiguration on macOS, Podman, or SSH-based Docker hosts", @@ -38,7 +39,7 @@ def test_missing_on_private_registry(monkeypatch): registry_url = registry.get_registry() # prepare auth config - creds: bytes = base64.b64encode(f"{username}:{password}".encode("utf-8")) + creds: bytes = base64.b64encode(f"{username}:{password}".encode()) config = {"auths": {f"{registry_url}": {"auth": creds.decode("utf-8")}}} monkeypatch.setattr(testcontainers_config, name="docker_auth_config", value=json.dumps(config)) assert testcontainers_config.docker_auth_config, "docker_auth_config not set" @@ -78,7 +79,7 @@ def test_with_private_registry(image, tag, username, password, expected_output, client.images.remove(f"{registry_url}/{image}:{tag}") # prepare auth config - creds: bytes = base64.b64encode(f"{username}:{password}".encode("utf-8")) + creds: bytes = base64.b64encode(f"{username}:{password}".encode()) config = {"auths": {f"{registry_url}": {"auth": creds.decode("utf-8")}}} monkeypatch.setattr(testcontainers_config, name="docker_auth_config", value=json.dumps(config)) assert testcontainers_config.docker_auth_config, "docker_auth_config not set" diff --git a/tests/core/test_docker_client.py b/tests/core/test_docker_client.py index 34b449a9b..990d0afd4 100644 --- a/tests/core/test_docker_client.py +++ b/tests/core/test_docker_client.py @@ -1,5 +1,5 @@ -import os import json +import os from collections import namedtuple from typing import Any from unittest import mock @@ -7,17 +7,15 @@ import docker import pytest +from docker.models.networks import Network -from testcontainers.core.config import testcontainers_config as c, ConnectionMode +from testcontainers.core import utils +from testcontainers.core.auth import parse_docker_auth_config +from testcontainers.core.config import ConnectionMode +from testcontainers.core.config import testcontainers_config as c from testcontainers.core.container import DockerContainer from testcontainers.core.docker_client import DockerClient, is_ssh_docker_host -from testcontainers.core.auth import parse_docker_auth_config from testcontainers.core.image import DockerImage -from testcontainers.core import utils - -from pytest import mark - -from docker.models.networks import Network def _expected_from_env_kwargs(**kwargs: Any) -> dict[str, Any]: @@ -99,7 +97,7 @@ def test_docker_client_login_empty_parse_docker_auth_config(): # This is used to make sure we don't fail (nor try to login) when we have unsupported auth config -@mark.parametrize("auth_config_sample", [{"credHelpers": {"test": "login"}}, {"credsStore": "login"}]) +@pytest.mark.parametrize("auth_config_sample", [{"credHelpers": {"test": "login"}}, {"credsStore": "login"}]) def test_docker_client_login_unsupported_auth_config(auth_config_sample): mock_docker = MagicMock(spec=docker) mock_get_docker_auth_config = MagicMock() diff --git a/tests/core/test_docker_in_docker.py b/tests/core/test_docker_in_docker.py index 992d53ae1..79bebe012 100644 --- a/tests/core/test_docker_in_docker.py +++ b/tests/core/test_docker_in_docker.py @@ -1,23 +1,23 @@ import contextlib import json import os -import time import socket import sys +import time +from collections.abc import Generator from pathlib import Path -from typing import Final, Any, Generator +from typing import Any, Final import pytest from docker.models.containers import Container from testcontainers.core import utils from testcontainers.core.config import testcontainers_config as tcc +from testcontainers.core.container import DockerContainer +from testcontainers.core.docker_client import LOGGER, DockerClient, is_ssh_docker_host from testcontainers.core.labels import SESSION_ID from testcontainers.core.network import Network -from testcontainers.core.container import DockerContainer -from testcontainers.core.docker_client import DockerClient, LOGGER, is_ssh_docker_host -from testcontainers.core.utils import inside_container -from testcontainers.core.utils import is_mac +from testcontainers.core.utils import inside_container, is_mac from testcontainers.core.waiting_utils import wait_for_logs _DIND_PYTHON_VERSION = (3, 13) @@ -51,7 +51,7 @@ def _wait_for_dind_return_ip(client: DockerClient, dind: Container): break except ConnectionRefusedError: if time.perf_counter() - start_wait > timeout: - raise RuntimeError("Docker in docker took longer than 10 seconds to start") + raise RuntimeError("Docker in docker took longer than 10 seconds to start") from None time.sleep(0.01) return docker_host_ip @@ -80,7 +80,7 @@ def test_wait_for_logs_docker_in_docker(): ) as container: assert container.get_container_host_ip() == docker_host_ip wait_for_logs(container, "Hello from Docker!") - stdout, stderr = container.get_logs() + stdout, _stderr = container.get_logs() assert stdout, "There should be something on stdout" not_really_dind.stop() @@ -123,7 +123,7 @@ def test_dind_inherits_network(): assert dind_id is not None assert container.get_docker_client().gateway_ip(wrapped_id) == client.gateway_ip(dind_id) wait_for_logs(container, "Hello from Docker!") - stdout, stderr = container.get_logs() + stdout, _stderr = container.get_logs() assert stdout, "There should be something on stdout" not_really_dind.stop() diff --git a/tests/core/test_image.py b/tests/core/test_image.py index 3a4e51f9b..38efe7709 100644 --- a/tests/core/test_image.py +++ b/tests/core/test_image.py @@ -1,11 +1,11 @@ -import pytest -import tempfile -import random import os - +import random +import tempfile from pathlib import Path from typing import Any, Optional +import pytest + from testcontainers.core.container import DockerContainer from testcontainers.core.image import DockerImage @@ -66,7 +66,7 @@ def test_docker_image_with_custom_dockerfile_path(dockerfile_path: Optional[Path with open(temp_dir_path / dockerfile_rel_path, "x") as f: f.write( - f""" + """ FROM alpine:latest CMD echo "Hello world!" """ @@ -79,14 +79,14 @@ def test_docker_image_with_custom_dockerfile_path(dockerfile_path: Optional[Path assert c_c assert c_c.image is not None assert c_c.image.short_id.endswith(image_short_id), "Image ID mismatch" - assert container.get_logs() == (("Hello world!\n").encode(), b""), "Container logs mismatch" + assert container.get_logs() == ((b"Hello world!\n"), b""), "Container logs mismatch" def test_docker_image_with_kwargs(): with tempfile.TemporaryDirectory() as temp_directory: with open(f"{temp_directory}/Dockerfile", "w") as f: f.write( - f""" + """ FROM alpine:latest ARG TEST_ARG ENV TEST_ARG $TEST_ARG @@ -103,4 +103,4 @@ def test_docker_image_with_kwargs(): assert c_c assert c_c.image is not None assert c_c.image.short_id.endswith(image_short_id), "Image ID mismatch" - assert container.get_logs() == (("new_arg\n").encode(), b""), "Container logs mismatch" + assert container.get_logs() == ((b"new_arg\n"), b""), "Container logs mismatch" diff --git a/tests/core/test_inspect.py b/tests/core/test_inspect.py index 0baf0dc5d..bd803347d 100644 --- a/tests/core/test_inspect.py +++ b/tests/core/test_inspect.py @@ -5,7 +5,6 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.docker_client import DockerClient from testcontainers.core.inspect import ContainerInspectInfo -from testcontainers.core.config import ConnectionMode FAKE_ID = "ABC123" diff --git a/tests/core/test_labels.py b/tests/core/test_labels.py index c34baaeef..8af9a8655 100644 --- a/tests/core/test_labels.py +++ b/tests/core/test_labels.py @@ -1,13 +1,14 @@ +import pytest + +from testcontainers.core.config import testcontainers_config as config from testcontainers.core.labels import ( LABEL_LANG, LABEL_SESSION_ID, LABEL_TESTCONTAINERS, LABEL_VERSION, - create_labels, TESTCONTAINERS_NAMESPACE, + create_labels, ) -import pytest -from testcontainers.core.config import testcontainers_config as config def assert_in_with_value(labels: dict[str, str], label: str, value: str, known_before_test_time: bool): diff --git a/tests/core/test_network.py b/tests/core/test_network.py index 0af690dcf..c84b30762 100644 --- a/tests/core/test_network.py +++ b/tests/core/test_network.py @@ -1,12 +1,13 @@ from http import HTTPStatus + +import docker.errors +import pytest + from testcontainers.core.container import DockerContainer from testcontainers.core.docker_client import DockerClient from testcontainers.core.labels import LABEL_SESSION_ID from testcontainers.core.network import Network -import docker.errors -import pytest - NGINX_ALPINE_SLIM_IMAGE = "nginx:1.25.4-alpine-slim" @@ -75,7 +76,7 @@ def test_containers_can_communicate_over_network(): def assert_can_ping(container: DockerContainer, remote_name: str): - status, output = container.exec("ping -c 1 %s" % remote_name) + status, output = container.exec(f"ping -c 1 {remote_name}") assert status == 0 assert "64 bytes" in str(output) diff --git a/tests/core/test_protocol_compliance.py b/tests/core/test_protocol_compliance.py index b3fb87bd1..57f1642e1 100644 --- a/tests/core/test_protocol_compliance.py +++ b/tests/core/test_protocol_compliance.py @@ -1,11 +1,8 @@ """Test protocol compliance for wait strategy targets.""" -import pytest -from typing import get_type_hints - -from testcontainers.core.waiting_utils import WaitStrategyTarget -from testcontainers.core.container import DockerContainer from testcontainers.compose.compose import ComposeContainer +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import WaitStrategyTarget def test_docker_container_implements_wait_strategy_target(): diff --git a/tests/core/test_ryuk.py b/tests/core/test_ryuk.py index 18d2a1c29..81bb736ca 100644 --- a/tests/core/test_ryuk.py +++ b/tests/core/test_ryuk.py @@ -1,13 +1,11 @@ -from time import sleep, perf_counter -import pytest -from pytest import MonkeyPatch +from time import perf_counter, sleep +import pytest from docker import DockerClient from docker.errors import NotFound from testcontainers.core.config import testcontainers_config -from testcontainers.core.container import Reaper -from testcontainers.core.container import DockerContainer +from testcontainers.core.container import DockerContainer, Reaper from testcontainers.core.utils import is_mac from testcontainers.core.waiting_utils import wait_for_logs @@ -38,7 +36,7 @@ def _wait_for_container_removed(client: DockerClient, container_id: str, timeout reason="Ryuk container reaping is unreliable on Docker Desktop for macOS due to VM-based container lifecycle handling", ) @pytest.mark.inside_docker_check -def test_wait_for_reaper(monkeypatch: MonkeyPatch): +def test_wait_for_reaper(monkeypatch: pytest.MonkeyPatch): Reaper.delete_instance() monkeypatch.setattr(testcontainers_config, "ryuk_reconnection_timeout", "0.1s") container = DockerContainer("hello-world") @@ -78,7 +76,7 @@ def test_wait_for_reaper(monkeypatch: MonkeyPatch): is_mac(), reason="Ryuk disabling behavior is unreliable on Docker Desktop for macOS due to Docker socket emulation" ) @pytest.mark.inside_docker_check -def test_container_without_ryuk(monkeypatch: MonkeyPatch): +def test_container_without_ryuk(monkeypatch: pytest.MonkeyPatch): Reaper.delete_instance() monkeypatch.setattr(testcontainers_config, "ryuk_disabled", True) with DockerContainer("hello-world") as container: diff --git a/tests/core/test_socat.py b/tests/core/test_socat.py index fb7c91b64..7852ed072 100644 --- a/tests/core/test_socat.py +++ b/tests/core/test_socat.py @@ -18,7 +18,7 @@ def test_socat_with_helloworld(): ): socat_url = f"http://{socat.get_container_host_ip()}:{socat.get_exposed_port(8080)}" - response = requests.get(f"{socat_url}/ping") # noqa: S113 + response = requests.get(f"{socat_url}/ping") assert response.status_code == 200 assert response.content == b"PONG" diff --git a/tests/core/test_transferable.py b/tests/core/test_transferable.py index 592ad87df..64d4da8be 100644 --- a/tests/core/test_transferable.py +++ b/tests/core/test_transferable.py @@ -1,13 +1,13 @@ +import io +import tarfile from pathlib import Path +from typing import Any import pytest + from testcontainers.core.container import DockerContainer from testcontainers.core.transferable import Transferable, TransferSpec, build_transfer_tar -import io -import tarfile -from typing import Any - def test_build_transfer_tar_from_bytes(): data = b"hello world" diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 4179242a3..c4fdcfbc8 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -1,7 +1,6 @@ from pathlib import Path import pytest -from pytest import MonkeyPatch, raises, mark import testcontainers.core.utils as utils @@ -10,29 +9,31 @@ def test_setup_logger() -> None: assert utils.setup_logger("test") is not None -@mark.parametrize("platform, expected", [("linux", "linux"), ("linux2", "linux"), ("darwin", "mac"), ("win32", "win")]) -def test_os_name(monkeypatch: MonkeyPatch, platform: str, expected: str) -> None: +@pytest.mark.parametrize( + "platform, expected", [("linux", "linux"), ("linux2", "linux"), ("darwin", "mac"), ("win32", "win")] +) +def test_os_name(monkeypatch: pytest.MonkeyPatch, platform: str, expected: str) -> None: assert utils.os_name() is not None monkeypatch.setattr("sys.platform", platform) assert utils.os_name() == expected -def test_is_mac(monkeypatch: MonkeyPatch) -> None: +def test_is_mac(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr("testcontainers.core.utils.os_name", lambda: "mac") assert utils.is_mac() -def test_is_linux(monkeypatch: MonkeyPatch) -> None: +def test_is_linux(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr("testcontainers.core.utils.os_name", lambda: "linux") assert utils.is_linux() -def test_is_windows(monkeypatch: MonkeyPatch) -> None: +def test_is_windows(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr("testcontainers.core.utils.os_name", lambda: "win") assert utils.is_windows() -def test_is_arm(monkeypatch: MonkeyPatch) -> None: +def test_is_arm(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr("platform.machine", lambda: "x86_64") assert not utils.is_arm() monkeypatch.setattr("platform.machine", lambda: "arm64") @@ -41,7 +42,7 @@ def test_is_arm(monkeypatch: MonkeyPatch) -> None: assert utils.is_arm() -def test_inside_container(monkeypatch: MonkeyPatch) -> None: +def test_inside_container(monkeypatch: pytest.MonkeyPatch) -> None: assert not utils.inside_container() monkeypatch.setattr("os.path.exists", lambda _: True) assert utils.inside_container() @@ -51,7 +52,7 @@ def test_raise_for_deprecated_parameters() -> None: kwargs = {"key": "value"} current = "key" replacement = "new_key" - with raises(ValueError) as e: + with pytest.raises(ValueError) as e: result = utils.raise_for_deprecated_parameter(kwargs, current, replacement) assert str(e.value) == "Parameter 'deprecated' is deprecated and should be replaced by 'replacement'." assert result == {} diff --git a/tests/core/test_wait_strategies.py b/tests/core/test_wait_strategies.py index 20f0e2c2c..2f5f3d408 100644 --- a/tests/core/test_wait_strategies.py +++ b/tests/core/test_wait_strategies.py @@ -1,21 +1,21 @@ +import itertools import logging import re import time from datetime import timedelta -from unittest.mock import Mock, patch, MagicMock +from unittest.mock import Mock, patch + import pytest -import itertools -from testcontainers.core.container import DockerContainer from testcontainers.core.wait_strategies import ( CompositeWaitStrategy, - WaitStrategyTarget, FileExistsWaitStrategy, HealthcheckWaitStrategy, HttpWaitStrategy, LogMessageWaitStrategy, PortWaitStrategy, WaitStrategy, + WaitStrategyTarget, ) @@ -485,7 +485,7 @@ def test_wait_until_ready(self, mock_sleep, mock_time, mock_socket, connection_s strategy.wait_until_ready(mock_container) mock_socket_instance.connect.assert_called_once_with(("localhost", 8080)) else: - with pytest.raises(TimeoutError, match="Port 8080 not available within 1.0 seconds"): + with pytest.raises(TimeoutError, match=r"Port 8080 not available within 1.0 seconds"): strategy.wait_until_ready(mock_container) @@ -547,7 +547,7 @@ def test_wait_until_ready(self, mock_sleep, mock_time, mock_is_file, file_exists strategy.wait_until_ready(mock_container) mock_is_file.assert_called() else: - with pytest.raises(TimeoutError, match="File.*did not exist within.*seconds"): + with pytest.raises(TimeoutError, match=r"File.*did not exist within.*seconds"): with caplog.at_level(logging.CRITICAL, logger="testcontainers.core.wait_strategies"): strategy.wait_until_ready(mock_container) diff --git a/tests/core/test_wait_strategies_integration.py b/tests/core/test_wait_strategies_integration.py index 4e090ab80..e54fc7c9b 100644 --- a/tests/core/test_wait_strategies_integration.py +++ b/tests/core/test_wait_strategies_integration.py @@ -1,5 +1,3 @@ -import tempfile -import time from pathlib import Path import pytest @@ -34,10 +32,10 @@ class TestDockerComposeIntegration: def test_compose_service_wait_strategies(self): """Test that wait strategies work with Docker Compose services.""" - from testcontainers.compose import DockerCompose - import tempfile from pathlib import Path + from testcontainers.compose import DockerCompose + # Use basic_multiple fixture with two alpine services that output logs compose = DockerCompose( context=Path(__file__).parent / "compose_fixtures" / "basic_multiple", @@ -72,7 +70,6 @@ def test_compose_service_wait_strategies(self): def test_compose_wait_strategy_timeout(self): """Test that compose wait strategies properly timeout.""" from testcontainers.compose import DockerCompose - from pathlib import Path compose = DockerCompose( context=Path(__file__).parent / "compose_fixtures" / "basic", compose_file_name="docker-compose.yaml" diff --git a/tests/core/test_waiting_utils.py b/tests/core/test_waiting_utils.py index 635c58c4e..b3cc43056 100644 --- a/tests/core/test_waiting_utils.py +++ b/tests/core/test_waiting_utils.py @@ -2,7 +2,7 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.wait_strategies import ContainerStatusWaitStrategy -from testcontainers.core.waiting_utils import wait_for_logs, wait_for, wait_container_is_ready +from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs def test_wait_for_logs() -> None: From cfcc60659f64c85d4441084e7f7f0f001af47247 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Fri, 5 Jun 2026 22:51:24 +0200 Subject: [PATCH 23/29] fix(doctests): Ensure paths are correct --- docs/community/generic.rst | 4 ++-- docs/conf.py | 6 ++++++ docs/core/README.rst | 2 +- doctests/conf.py | 7 +++++++ src/testcontainers/community/aws/aws_lambda.py | 2 +- src/testcontainers/community/generic/server.py | 2 +- src/testcontainers/community/mysql/__init__.py | 7 ++++--- src/testcontainers/community/sftp/__init__.py | 4 ++-- src/testcontainers/compose/compose.py | 2 +- src/testcontainers/core/image.py | 2 +- 10 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/community/generic.rst b/docs/community/generic.rst index 5bec0092c..33e8b31f0 100644 --- a/docs/community/generic.rst +++ b/docs/community/generic.rst @@ -11,7 +11,7 @@ FastAPI container that is using :code:`ServerContainer` >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./tests/community/generic/samples/fastapi", tag="fastapi-test:latest") as image: + >>> with DockerImage(path=f"{TEST_DIR}/community/generic/samples/fastapi", tag="fastapi-test:latest") as image: ... with ServerContainer(port=80, image=image) as fastapi_server: ... delay = wait_for_logs(fastapi_server, "Uvicorn running on http://0.0.0.0:80") ... fastapi_server.get_api_url = lambda: fastapi_server._create_connection_url() + "/api/v1/" @@ -31,7 +31,7 @@ A more advance use-case, where we are using a FastAPI container that is using Re ... redis_container_port = redis.port ... redis_container_ip_address = redis.get_docker_client().bridge_ip(redis._container.id) - ... with DockerImage(path="./tests/community/generic/samples/advance_1", tag="advance-1:latest") as image: + ... with DockerImage(path=f"{TEST_DIR}/community/generic/samples/advance_1", tag="advance-1:latest") as image: ... web_server = ServerContainer(port=80, image=image) ... web_server.with_env(key="REDIS_HOST", value=redis_container_ip_address) ... web_server.with_env(key="REDIS_PORT", value=redis_container_port) diff --git a/docs/conf.py b/docs/conf.py index 3c37b2bff..2f342cd16 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,6 +18,10 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) +from pathlib import Path + +BASE_DIR = Path(__file__).parent.parent.resolve() +TEST_DIR = BASE_DIR / "tests" # -- General configuration ------------------------------------------------ @@ -170,3 +174,5 @@ ("py:class", "docker.models.containers.ExecResult"), ("py:class", "testcontainers.core.docker_client.ContainerInspectInfo"), ] + +doctest_global_setup = f"TEST_DIR = '{TEST_DIR}'" diff --git a/docs/core/README.rst b/docs/core/README.rst index 8fc2aa16e..2358838ac 100644 --- a/docs/core/README.rst +++ b/docs/core/README.rst @@ -45,7 +45,7 @@ Using `DockerContainer` and `DockerImage` to create a container: >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./core/tests/image_fixtures/sample/", tag="test-sample:latest") as image: + >>> with DockerImage(path=f"{TEST_DIR}/core/image_fixtures/sample/", tag="test-sample:latest") as image: ... with DockerContainer(str(image)) as container: ... delay = wait_for_logs(container, "Test Sample Image") diff --git a/doctests/conf.py b/doctests/conf.py index 0822df226..c71b119d7 100644 --- a/doctests/conf.py +++ b/doctests/conf.py @@ -1,5 +1,12 @@ +from pathlib import Path + +BASE_DIR = Path(__file__).parent.parent.resolve() +TEST_DIR = BASE_DIR / "tests" + extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", ] master_doc = "README" + +doctest_global_setup = f"TEST_DIR = '{TEST_DIR}'" diff --git a/src/testcontainers/community/aws/aws_lambda.py b/src/testcontainers/community/aws/aws_lambda.py index 3b5a71b94..d2c53b5b7 100644 --- a/src/testcontainers/community/aws/aws_lambda.py +++ b/src/testcontainers/community/aws/aws_lambda.py @@ -24,7 +24,7 @@ class AWSLambdaContainer(ServerContainer): >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./modules/aws/tests/lambda_sample", tag="test-lambda:latest") as image: + >>> with DockerImage(path=f"{TEST_DIR}/community/aws/lambda_sample", tag="test-lambda:latest") as image: ... with AWSLambdaContainer(image=image, port=8080) as func: ... response = func.send_request(data={'payload': 'some data'}) ... assert response.status_code == 200 diff --git a/src/testcontainers/community/generic/server.py b/src/testcontainers/community/generic/server.py index 3c6c6ccfe..4c8e12778 100644 --- a/src/testcontainers/community/generic/server.py +++ b/src/testcontainers/community/generic/server.py @@ -21,7 +21,7 @@ class ServerContainer(DockerContainer): >>> from testcontainers.core.waiting_utils import wait_for_logs >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./modules/generic/tests/samples/python_server", tag="test-srv:latest") as image: + >>> with DockerImage(path=f"{TEST_DIR}/community/generic/samples/python_server", tag="test-srv:latest") as image: ... with ServerContainer(port=9000, image=image) as srv: ... url = srv._create_connection_url() ... response = httpx.get(f"{url}", timeout=5) diff --git a/src/testcontainers/community/mysql/__init__.py b/src/testcontainers/community/mysql/__init__.py index e6a37ebb4..503545b17 100644 --- a/src/testcontainers/community/mysql/__init__.py +++ b/src/testcontainers/community/mysql/__init__.py @@ -38,7 +38,7 @@ class MySqlContainer(DbContainer): >>> import sqlalchemy >>> from testcontainers.community.mysql import MySqlContainer - >>> with MySqlContainer("mysql:5.7.17", dialect="pymysql") as mysql: + >>> with MySqlContainer("mysql:8.0", dialect="pymysql") as mysql: ... engine = sqlalchemy.create_engine(mysql.get_connection_url()) ... with engine.begin() as connection: ... result = connection.execute(sqlalchemy.text("select version()")) @@ -50,12 +50,13 @@ class MySqlContainer(DbContainer): automatically. .. doctest:: + >>> import sqlalchemy >>> from testcontainers.community.mysql import MySqlContainer - >>> with MySqlContainer(seed="../../tests/seeds/") as mysql: + >>> with MySqlContainer(seed=f"{TEST_DIR}/community/mysql/seeds/", dialect="pymysql") as mysql: ... engine = sqlalchemy.create_engine(mysql.get_connection_url()) ... with engine.begin() as connection: - ... query = "select * from stuff" # Can now rely on schema/data + ... query = "select name from stuff" ... result = connection.execute(sqlalchemy.text(query)) ... first_stuff, = result.fetchone() diff --git a/src/testcontainers/community/sftp/__init__.py b/src/testcontainers/community/sftp/__init__.py index ae299fcde..f1b54982e 100644 --- a/src/testcontainers/community/sftp/__init__.py +++ b/src/testcontainers/community/sftp/__init__.py @@ -199,7 +199,7 @@ class SFTPContainer(DockerContainer): ... host_port = sftp_container.get_exposed_sftp_port() ... ssh = paramiko.SSHClient() ... ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ... ssh.connect(host_ip, host_port, "basic", "password") + ... ssh.connect(host_ip, host_port, "basic", "password", allow_agent=False, look_for_keys=False) ... # ssh.get(...) ... # ssh.listdir() ... # ssh.chdir("upload") @@ -219,7 +219,7 @@ class SFTPContainer(DockerContainer): ... ssh = paramiko.SSHClient() ... ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ... private_key_file = sftp_container.users[1].private_key_file - ... ssh.connect(host_ip, host_port, "keypair", key_filename=private_key_file) + ... ssh.connect(host_ip, host_port, "keypair", key_filename=private_key_file, allow_agent=False) ... # ssh.listdir() ... # ssh.get(...) ... # ssh.chdir("upload") diff --git a/src/testcontainers/compose/compose.py b/src/testcontainers/compose/compose.py index 38a7dfd0a..9d3a17671 100644 --- a/src/testcontainers/compose/compose.py +++ b/src/testcontainers/compose/compose.py @@ -232,7 +232,7 @@ class DockerCompose: >>> from testcontainers.compose import DockerCompose - >>> compose = DockerCompose("core/tests/compose_fixtures/basic", compose_file_name="hello.yaml", + >>> compose = DockerCompose(f"{TEST_DIR}/core/compose_fixtures/basic", compose_file_name="hello.yaml", ... pull=True) >>> with compose: ... stdout, stderr = compose.get_logs() diff --git a/src/testcontainers/core/image.py b/src/testcontainers/core/image.py index 9a39ec1f3..329ebedca 100644 --- a/src/testcontainers/core/image.py +++ b/src/testcontainers/core/image.py @@ -27,7 +27,7 @@ class DockerImage: >>> from testcontainers.core.image import DockerImage - >>> with DockerImage(path="./core/tests/image_fixtures/sample/", tag="test-image") as image: + >>> with DockerImage(path=f"{TEST_DIR}/core/image_fixtures/sample/", tag="test-image") as image: ... logs = image.get_logs() :param path: Path to the build context From 015e34ec916d20f248ac76d17ea64848ffb11574 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Fri, 5 Jun 2026 23:09:34 +0200 Subject: [PATCH 24/29] chore(main): remove module folder completely --- modules/index.rst | 11 ----------- {modules => src/testcontainers/community}/README.md | 0 2 files changed, 11 deletions(-) delete mode 100644 modules/index.rst rename {modules => src/testcontainers/community}/README.md (100%) diff --git a/modules/index.rst b/modules/index.rst deleted file mode 100644 index d2a67a3d4..000000000 --- a/modules/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -Community Modules -================= - -.. - glob: - https://stackoverflow.com/a/44572883/4971476 - -.. toctree:: - :glob: - - */README diff --git a/modules/README.md b/src/testcontainers/community/README.md similarity index 100% rename from modules/README.md rename to src/testcontainers/community/README.md From c4a14760a7c19c733851f27f836b3446c6032755 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Fri, 5 Jun 2026 23:45:05 +0200 Subject: [PATCH 25/29] feat(main): make legacy imports available with deprecation notice --- src/testcontainers/arangodb.py | 15 +++++ src/testcontainers/aws.py | 15 +++++ src/testcontainers/azurite.py | 17 ++++++ src/testcontainers/cassandra.py | 15 +++++ src/testcontainers/chroma.py | 15 +++++ src/testcontainers/clickhouse.py | 15 +++++ src/testcontainers/cockroachdb.py | 15 +++++ src/testcontainers/cosmosdb.py | 17 ++++++ src/testcontainers/db2.py | 15 +++++ src/testcontainers/elasticsearch.py | 15 +++++ src/testcontainers/generic.py | 17 ++++++ src/testcontainers/google.py | 17 ++++++ src/testcontainers/influxdb.py | 15 +++++ src/testcontainers/influxdb1.py | 15 +++++ src/testcontainers/influxdb2.py | 15 +++++ src/testcontainers/k3s.py | 15 +++++ src/testcontainers/kafka.py | 19 ++++++ src/testcontainers/keycloak.py | 15 +++++ src/testcontainers/localstack.py | 15 +++++ src/testcontainers/mailpit.py | 17 ++++++ src/testcontainers/memcached.py | 17 ++++++ src/testcontainers/milvus.py | 15 +++++ src/testcontainers/minio.py | 15 +++++ src/testcontainers/mongodb.py | 17 ++++++ src/testcontainers/mqtt.py | 15 +++++ src/testcontainers/mssql.py | 15 +++++ src/testcontainers/mysql.py | 15 +++++ src/testcontainers/nats.py | 15 +++++ src/testcontainers/neo4j.py | 15 +++++ src/testcontainers/nginx.py | 15 +++++ src/testcontainers/ollama.py | 17 ++++++ src/testcontainers/openfga.py | 15 +++++ src/testcontainers/opensearch.py | 15 +++++ src/testcontainers/oracle.py | 15 +++++ src/testcontainers/postgres.py | 15 +++++ src/testcontainers/qdrant.py | 15 +++++ src/testcontainers/rabbitmq.py | 15 +++++ src/testcontainers/redis.py | 19 ++++++ src/testcontainers/registry.py | 15 +++++ src/testcontainers/scylla.py | 15 +++++ src/testcontainers/selenium.py | 19 ++++++ src/testcontainers/sftp.py | 17 ++++++ src/testcontainers/trino.py | 15 +++++ src/testcontainers/valkey.py | 15 +++++ src/testcontainers/vault.py | 15 +++++ src/testcontainers/weaviate.py | 15 +++++ tests/core/test_legacy_module_imports.py | 74 ++++++++++++++++++++++++ 47 files changed, 794 insertions(+) create mode 100644 src/testcontainers/arangodb.py create mode 100644 src/testcontainers/aws.py create mode 100644 src/testcontainers/azurite.py create mode 100644 src/testcontainers/cassandra.py create mode 100644 src/testcontainers/chroma.py create mode 100644 src/testcontainers/clickhouse.py create mode 100644 src/testcontainers/cockroachdb.py create mode 100644 src/testcontainers/cosmosdb.py create mode 100644 src/testcontainers/db2.py create mode 100644 src/testcontainers/elasticsearch.py create mode 100644 src/testcontainers/generic.py create mode 100644 src/testcontainers/google.py create mode 100644 src/testcontainers/influxdb.py create mode 100644 src/testcontainers/influxdb1.py create mode 100644 src/testcontainers/influxdb2.py create mode 100644 src/testcontainers/k3s.py create mode 100644 src/testcontainers/kafka.py create mode 100644 src/testcontainers/keycloak.py create mode 100644 src/testcontainers/localstack.py create mode 100644 src/testcontainers/mailpit.py create mode 100644 src/testcontainers/memcached.py create mode 100644 src/testcontainers/milvus.py create mode 100644 src/testcontainers/minio.py create mode 100644 src/testcontainers/mongodb.py create mode 100644 src/testcontainers/mqtt.py create mode 100644 src/testcontainers/mssql.py create mode 100644 src/testcontainers/mysql.py create mode 100644 src/testcontainers/nats.py create mode 100644 src/testcontainers/neo4j.py create mode 100644 src/testcontainers/nginx.py create mode 100644 src/testcontainers/ollama.py create mode 100644 src/testcontainers/openfga.py create mode 100644 src/testcontainers/opensearch.py create mode 100644 src/testcontainers/oracle.py create mode 100644 src/testcontainers/postgres.py create mode 100644 src/testcontainers/qdrant.py create mode 100644 src/testcontainers/rabbitmq.py create mode 100644 src/testcontainers/redis.py create mode 100644 src/testcontainers/registry.py create mode 100644 src/testcontainers/scylla.py create mode 100644 src/testcontainers/selenium.py create mode 100644 src/testcontainers/sftp.py create mode 100644 src/testcontainers/trino.py create mode 100644 src/testcontainers/valkey.py create mode 100644 src/testcontainers/vault.py create mode 100644 src/testcontainers/weaviate.py create mode 100644 tests/core/test_legacy_module_imports.py diff --git a/src/testcontainers/arangodb.py b/src/testcontainers/arangodb.py new file mode 100644 index 000000000..c371146c5 --- /dev/null +++ b/src/testcontainers/arangodb.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.arangodb import ( + ArangoDbContainer, +) + +warnings.warn( + "testcontainers.arangodb is deprecated, use testcontainers.community.arangodb instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ArangoDbContainer", +] diff --git a/src/testcontainers/aws.py b/src/testcontainers/aws.py new file mode 100644 index 000000000..3562101bb --- /dev/null +++ b/src/testcontainers/aws.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.aws import ( + AWSLambdaContainer, +) + +warnings.warn( + "testcontainers.aws is deprecated, use testcontainers.community.aws instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "AWSLambdaContainer", +] diff --git a/src/testcontainers/azurite.py b/src/testcontainers/azurite.py new file mode 100644 index 000000000..dd3b64419 --- /dev/null +++ b/src/testcontainers/azurite.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.azurite import ( + AzuriteContainer, + ConnectionStringType, +) + +warnings.warn( + "testcontainers.azurite is deprecated, use testcontainers.community.azurite instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "AzuriteContainer", + "ConnectionStringType", +] diff --git a/src/testcontainers/cassandra.py b/src/testcontainers/cassandra.py new file mode 100644 index 000000000..a936f95de --- /dev/null +++ b/src/testcontainers/cassandra.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.cassandra import ( + CassandraContainer, +) + +warnings.warn( + "testcontainers.cassandra is deprecated, use testcontainers.community.cassandra instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "CassandraContainer", +] diff --git a/src/testcontainers/chroma.py b/src/testcontainers/chroma.py new file mode 100644 index 000000000..21d9ea7ca --- /dev/null +++ b/src/testcontainers/chroma.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.chroma import ( + ChromaContainer, +) + +warnings.warn( + "testcontainers.chroma is deprecated, use testcontainers.community.chroma instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ChromaContainer", +] diff --git a/src/testcontainers/clickhouse.py b/src/testcontainers/clickhouse.py new file mode 100644 index 000000000..78c460e22 --- /dev/null +++ b/src/testcontainers/clickhouse.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.clickhouse import ( + ClickHouseContainer, +) + +warnings.warn( + "testcontainers.clickhouse is deprecated, use testcontainers.community.clickhouse instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ClickHouseContainer", +] diff --git a/src/testcontainers/cockroachdb.py b/src/testcontainers/cockroachdb.py new file mode 100644 index 000000000..39c8a32aa --- /dev/null +++ b/src/testcontainers/cockroachdb.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.cockroachdb import ( + CockroachDBContainer, +) + +warnings.warn( + "testcontainers.cockroachdb is deprecated, use testcontainers.community.cockroachdb instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "CockroachDBContainer", +] diff --git a/src/testcontainers/cosmosdb.py b/src/testcontainers/cosmosdb.py new file mode 100644 index 000000000..9625383a8 --- /dev/null +++ b/src/testcontainers/cosmosdb.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.cosmosdb import ( + CosmosDBMongoEndpointContainer, + CosmosDBNoSQLEndpointContainer, +) + +warnings.warn( + "testcontainers.cosmosdb is deprecated, use testcontainers.community.cosmosdb instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "CosmosDBMongoEndpointContainer", + "CosmosDBNoSQLEndpointContainer", +] diff --git a/src/testcontainers/db2.py b/src/testcontainers/db2.py new file mode 100644 index 000000000..f7aae6057 --- /dev/null +++ b/src/testcontainers/db2.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.db2 import ( + Db2Container, +) + +warnings.warn( + "testcontainers.db2 is deprecated, use testcontainers.community.db2 instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "Db2Container", +] diff --git a/src/testcontainers/elasticsearch.py b/src/testcontainers/elasticsearch.py new file mode 100644 index 000000000..2cd217d52 --- /dev/null +++ b/src/testcontainers/elasticsearch.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.elasticsearch import ( + ElasticSearchContainer, +) + +warnings.warn( + "testcontainers.elasticsearch is deprecated, use testcontainers.community.elasticsearch instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ElasticSearchContainer", +] diff --git a/src/testcontainers/generic.py b/src/testcontainers/generic.py new file mode 100644 index 000000000..2016fa442 --- /dev/null +++ b/src/testcontainers/generic.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.generic import ( + ServerContainer, + SqlContainer, +) + +warnings.warn( + "testcontainers.generic is deprecated, use testcontainers.community.generic instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ServerContainer", + "SqlContainer", +] diff --git a/src/testcontainers/google.py b/src/testcontainers/google.py new file mode 100644 index 000000000..dfc177cc7 --- /dev/null +++ b/src/testcontainers/google.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.google import ( + DatastoreContainer, + PubSubContainer, +) + +warnings.warn( + "testcontainers.google is deprecated, use testcontainers.community.google instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "DatastoreContainer", + "PubSubContainer", +] diff --git a/src/testcontainers/influxdb.py b/src/testcontainers/influxdb.py new file mode 100644 index 000000000..84abe4ae8 --- /dev/null +++ b/src/testcontainers/influxdb.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.influxdb import ( + InfluxDbContainer, +) + +warnings.warn( + "testcontainers.influxdb is deprecated, use testcontainers.community.influxdb instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "InfluxDbContainer", +] diff --git a/src/testcontainers/influxdb1.py b/src/testcontainers/influxdb1.py new file mode 100644 index 000000000..11a486628 --- /dev/null +++ b/src/testcontainers/influxdb1.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.influxdb1 import ( + InfluxDb1Container, +) + +warnings.warn( + "testcontainers.influxdb1 is deprecated, use testcontainers.community.influxdb1 instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "InfluxDb1Container", +] diff --git a/src/testcontainers/influxdb2.py b/src/testcontainers/influxdb2.py new file mode 100644 index 000000000..e9efb6765 --- /dev/null +++ b/src/testcontainers/influxdb2.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.influxdb2 import ( + InfluxDb2Container, +) + +warnings.warn( + "testcontainers.influxdb2 is deprecated, use testcontainers.community.influxdb2 instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "InfluxDb2Container", +] diff --git a/src/testcontainers/k3s.py b/src/testcontainers/k3s.py new file mode 100644 index 000000000..feebb0caf --- /dev/null +++ b/src/testcontainers/k3s.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.k3s import ( + K3SContainer, +) + +warnings.warn( + "testcontainers.k3s is deprecated, use testcontainers.community.k3s instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "K3SContainer", +] diff --git a/src/testcontainers/kafka.py b/src/testcontainers/kafka.py new file mode 100644 index 000000000..a79d501db --- /dev/null +++ b/src/testcontainers/kafka.py @@ -0,0 +1,19 @@ +import warnings + +from testcontainers.community.kafka import ( + KafkaContainer, + RedpandaContainer, + kafka_config, +) + +warnings.warn( + "testcontainers.kafka is deprecated, use testcontainers.community.kafka instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "KafkaContainer", + "RedpandaContainer", + "kafka_config", +] diff --git a/src/testcontainers/keycloak.py b/src/testcontainers/keycloak.py new file mode 100644 index 000000000..25f9c7c11 --- /dev/null +++ b/src/testcontainers/keycloak.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.keycloak import ( + KeycloakContainer, +) + +warnings.warn( + "testcontainers.keycloak is deprecated, use testcontainers.community.keycloak instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "KeycloakContainer", +] diff --git a/src/testcontainers/localstack.py b/src/testcontainers/localstack.py new file mode 100644 index 000000000..2dc549efb --- /dev/null +++ b/src/testcontainers/localstack.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.localstack import ( + LocalStackContainer, +) + +warnings.warn( + "testcontainers.localstack is deprecated, use testcontainers.community.localstack instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "LocalStackContainer", +] diff --git a/src/testcontainers/mailpit.py b/src/testcontainers/mailpit.py new file mode 100644 index 000000000..73463531e --- /dev/null +++ b/src/testcontainers/mailpit.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.mailpit import ( + MailpitContainer, + MailpitUser, +) + +warnings.warn( + "testcontainers.mailpit is deprecated, use testcontainers.community.mailpit instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MailpitContainer", + "MailpitUser", +] diff --git a/src/testcontainers/memcached.py b/src/testcontainers/memcached.py new file mode 100644 index 000000000..819448150 --- /dev/null +++ b/src/testcontainers/memcached.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.memcached import ( + MemcachedContainer, + MemcachedNotReady, +) + +warnings.warn( + "testcontainers.memcached is deprecated, use testcontainers.community.memcached instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MemcachedContainer", + "MemcachedNotReady", +] diff --git a/src/testcontainers/milvus.py b/src/testcontainers/milvus.py new file mode 100644 index 000000000..edf0e4963 --- /dev/null +++ b/src/testcontainers/milvus.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.milvus import ( + MilvusContainer, +) + +warnings.warn( + "testcontainers.milvus is deprecated, use testcontainers.community.milvus instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MilvusContainer", +] diff --git a/src/testcontainers/minio.py b/src/testcontainers/minio.py new file mode 100644 index 000000000..e25e997af --- /dev/null +++ b/src/testcontainers/minio.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.minio import ( + MinioContainer, +) + +warnings.warn( + "testcontainers.minio is deprecated, use testcontainers.community.minio instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MinioContainer", +] diff --git a/src/testcontainers/mongodb.py b/src/testcontainers/mongodb.py new file mode 100644 index 000000000..498eca702 --- /dev/null +++ b/src/testcontainers/mongodb.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.mongodb import ( + MongoDBAtlasLocalContainer, + MongoDbContainer, +) + +warnings.warn( + "testcontainers.mongodb is deprecated, use testcontainers.community.mongodb instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MongoDBAtlasLocalContainer", + "MongoDbContainer", +] diff --git a/src/testcontainers/mqtt.py b/src/testcontainers/mqtt.py new file mode 100644 index 000000000..2bbb5e214 --- /dev/null +++ b/src/testcontainers/mqtt.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.mqtt import ( + MosquittoContainer, +) + +warnings.warn( + "testcontainers.mqtt is deprecated, use testcontainers.community.mqtt instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MosquittoContainer", +] diff --git a/src/testcontainers/mssql.py b/src/testcontainers/mssql.py new file mode 100644 index 000000000..102f021bd --- /dev/null +++ b/src/testcontainers/mssql.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.mssql import ( + SqlServerContainer, +) + +warnings.warn( + "testcontainers.mssql is deprecated, use testcontainers.community.mssql instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "SqlServerContainer", +] diff --git a/src/testcontainers/mysql.py b/src/testcontainers/mysql.py new file mode 100644 index 000000000..7258e0573 --- /dev/null +++ b/src/testcontainers/mysql.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.mysql import ( + MySqlContainer, +) + +warnings.warn( + "testcontainers.mysql is deprecated, use testcontainers.community.mysql instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "MySqlContainer", +] diff --git a/src/testcontainers/nats.py b/src/testcontainers/nats.py new file mode 100644 index 000000000..d48827692 --- /dev/null +++ b/src/testcontainers/nats.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.nats import ( + NatsContainer, +) + +warnings.warn( + "testcontainers.nats is deprecated, use testcontainers.community.nats instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "NatsContainer", +] diff --git a/src/testcontainers/neo4j.py b/src/testcontainers/neo4j.py new file mode 100644 index 000000000..6d27e58a0 --- /dev/null +++ b/src/testcontainers/neo4j.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.neo4j import ( + Neo4jContainer, +) + +warnings.warn( + "testcontainers.neo4j is deprecated, use testcontainers.community.neo4j instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "Neo4jContainer", +] diff --git a/src/testcontainers/nginx.py b/src/testcontainers/nginx.py new file mode 100644 index 000000000..d433c9e0d --- /dev/null +++ b/src/testcontainers/nginx.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.nginx import ( + NginxContainer, +) + +warnings.warn( + "testcontainers.nginx is deprecated, use testcontainers.community.nginx instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "NginxContainer", +] diff --git a/src/testcontainers/ollama.py b/src/testcontainers/ollama.py new file mode 100644 index 000000000..aba69f731 --- /dev/null +++ b/src/testcontainers/ollama.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.ollama import ( + OllamaContainer, + OllamaModel, +) + +warnings.warn( + "testcontainers.ollama is deprecated, use testcontainers.community.ollama instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "OllamaContainer", + "OllamaModel", +] diff --git a/src/testcontainers/openfga.py b/src/testcontainers/openfga.py new file mode 100644 index 000000000..102f0a8e7 --- /dev/null +++ b/src/testcontainers/openfga.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.openfga import ( + OpenFGAContainer, +) + +warnings.warn( + "testcontainers.openfga is deprecated, use testcontainers.community.openfga instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "OpenFGAContainer", +] diff --git a/src/testcontainers/opensearch.py b/src/testcontainers/opensearch.py new file mode 100644 index 000000000..df3579cc6 --- /dev/null +++ b/src/testcontainers/opensearch.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.opensearch import ( + OpenSearchContainer, +) + +warnings.warn( + "testcontainers.opensearch is deprecated, use testcontainers.community.opensearch instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "OpenSearchContainer", +] diff --git a/src/testcontainers/oracle.py b/src/testcontainers/oracle.py new file mode 100644 index 000000000..0c2d238c5 --- /dev/null +++ b/src/testcontainers/oracle.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.oracle import ( + OracleDbContainer, +) + +warnings.warn( + "testcontainers.oracle is deprecated, use testcontainers.community.oracle instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "OracleDbContainer", +] diff --git a/src/testcontainers/postgres.py b/src/testcontainers/postgres.py new file mode 100644 index 000000000..f8603526d --- /dev/null +++ b/src/testcontainers/postgres.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.postgres import ( + PostgresContainer, +) + +warnings.warn( + "testcontainers.postgres is deprecated, use testcontainers.community.postgres instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "PostgresContainer", +] diff --git a/src/testcontainers/qdrant.py b/src/testcontainers/qdrant.py new file mode 100644 index 000000000..531ad99cb --- /dev/null +++ b/src/testcontainers/qdrant.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.qdrant import ( + QdrantContainer, +) + +warnings.warn( + "testcontainers.qdrant is deprecated, use testcontainers.community.qdrant instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "QdrantContainer", +] diff --git a/src/testcontainers/rabbitmq.py b/src/testcontainers/rabbitmq.py new file mode 100644 index 000000000..053929b29 --- /dev/null +++ b/src/testcontainers/rabbitmq.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.rabbitmq import ( + RabbitMqContainer, +) + +warnings.warn( + "testcontainers.rabbitmq is deprecated, use testcontainers.community.rabbitmq instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "RabbitMqContainer", +] diff --git a/src/testcontainers/redis.py b/src/testcontainers/redis.py new file mode 100644 index 000000000..23f2839e7 --- /dev/null +++ b/src/testcontainers/redis.py @@ -0,0 +1,19 @@ +import warnings + +from testcontainers.community.redis import ( + AsyncRedisContainer, + PingWaitStrategy, + RedisContainer, +) + +warnings.warn( + "testcontainers.redis is deprecated, use testcontainers.community.redis instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "AsyncRedisContainer", + "PingWaitStrategy", + "RedisContainer", +] diff --git a/src/testcontainers/registry.py b/src/testcontainers/registry.py new file mode 100644 index 000000000..e57c7f0b7 --- /dev/null +++ b/src/testcontainers/registry.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.registry import ( + DockerRegistryContainer, +) + +warnings.warn( + "testcontainers.registry is deprecated, use testcontainers.community.registry instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "DockerRegistryContainer", +] diff --git a/src/testcontainers/scylla.py b/src/testcontainers/scylla.py new file mode 100644 index 000000000..643e13b06 --- /dev/null +++ b/src/testcontainers/scylla.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.scylla import ( + ScyllaContainer, +) + +warnings.warn( + "testcontainers.scylla is deprecated, use testcontainers.community.scylla instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ScyllaContainer", +] diff --git a/src/testcontainers/selenium.py b/src/testcontainers/selenium.py new file mode 100644 index 000000000..27c670ebe --- /dev/null +++ b/src/testcontainers/selenium.py @@ -0,0 +1,19 @@ +import warnings + +from testcontainers.community.selenium import ( + BrowserWebDriverContainer, + SeleniumVideoContainer, + get_image_name, +) + +warnings.warn( + "testcontainers.selenium is deprecated, use testcontainers.community.selenium instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "BrowserWebDriverContainer", + "SeleniumVideoContainer", + "get_image_name", +] diff --git a/src/testcontainers/sftp.py b/src/testcontainers/sftp.py new file mode 100644 index 000000000..6493af8b1 --- /dev/null +++ b/src/testcontainers/sftp.py @@ -0,0 +1,17 @@ +import warnings + +from testcontainers.community.sftp import ( + SFTPContainer, + SFTPUser, +) + +warnings.warn( + "testcontainers.sftp is deprecated, use testcontainers.community.sftp instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "SFTPContainer", + "SFTPUser", +] diff --git a/src/testcontainers/trino.py b/src/testcontainers/trino.py new file mode 100644 index 000000000..4af86818b --- /dev/null +++ b/src/testcontainers/trino.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.trino import ( + TrinoContainer, +) + +warnings.warn( + "testcontainers.trino is deprecated, use testcontainers.community.trino instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "TrinoContainer", +] diff --git a/src/testcontainers/valkey.py b/src/testcontainers/valkey.py new file mode 100644 index 000000000..10b150ef8 --- /dev/null +++ b/src/testcontainers/valkey.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.valkey import ( + ValkeyContainer, +) + +warnings.warn( + "testcontainers.valkey is deprecated, use testcontainers.community.valkey instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "ValkeyContainer", +] diff --git a/src/testcontainers/vault.py b/src/testcontainers/vault.py new file mode 100644 index 000000000..50e48ff64 --- /dev/null +++ b/src/testcontainers/vault.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.vault import ( + VaultContainer, +) + +warnings.warn( + "testcontainers.vault is deprecated, use testcontainers.community.vault instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "VaultContainer", +] diff --git a/src/testcontainers/weaviate.py b/src/testcontainers/weaviate.py new file mode 100644 index 000000000..07915042e --- /dev/null +++ b/src/testcontainers/weaviate.py @@ -0,0 +1,15 @@ +import warnings + +from testcontainers.community.weaviate import ( + WeaviateContainer, +) + +warnings.warn( + "testcontainers.weaviate is deprecated, use testcontainers.community.weaviate instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "WeaviateContainer", +] diff --git a/tests/core/test_legacy_module_imports.py b/tests/core/test_legacy_module_imports.py new file mode 100644 index 000000000..e67cec49f --- /dev/null +++ b/tests/core/test_legacy_module_imports.py @@ -0,0 +1,74 @@ +import importlib +import sys + +import pytest + +MODULES = { + "arangodb": ["ArangoDbContainer"], + "aws": ["AWSLambdaContainer"], + "azurite": ["AzuriteContainer", "ConnectionStringType"], + "cassandra": ["CassandraContainer"], + "chroma": ["ChromaContainer"], + "clickhouse": ["ClickHouseContainer"], + "cockroachdb": ["CockroachDBContainer"], + "cosmosdb": ["CosmosDBMongoEndpointContainer", "CosmosDBNoSQLEndpointContainer"], + "db2": ["Db2Container"], + "elasticsearch": ["ElasticSearchContainer"], + "generic": ["ServerContainer", "SqlContainer"], + "google": ["DatastoreContainer", "PubSubContainer"], + "influxdb": ["InfluxDbContainer"], + "influxdb1": ["InfluxDb1Container"], + "influxdb2": ["InfluxDb2Container"], + "k3s": ["K3SContainer"], + "kafka": ["KafkaContainer", "RedpandaContainer", "kafka_config"], + "keycloak": ["KeycloakContainer"], + "localstack": ["LocalStackContainer"], + "mailpit": ["MailpitContainer", "MailpitUser"], + "memcached": ["MemcachedContainer", "MemcachedNotReady"], + "milvus": ["MilvusContainer"], + "minio": ["MinioContainer"], + "mongodb": ["MongoDbContainer", "MongoDBAtlasLocalContainer"], + "mqtt": ["MosquittoContainer"], + "mssql": ["SqlServerContainer"], + "mysql": ["MySqlContainer"], + "nats": ["NatsContainer"], + "neo4j": ["Neo4jContainer"], + "nginx": ["NginxContainer"], + "ollama": ["OllamaContainer", "OllamaModel"], + "openfga": ["OpenFGAContainer"], + "opensearch": ["OpenSearchContainer"], + "oracle": ["OracleDbContainer"], + "postgres": ["PostgresContainer"], + "qdrant": ["QdrantContainer"], + "rabbitmq": ["RabbitMqContainer"], + "redis": ["AsyncRedisContainer", "PingWaitStrategy", "RedisContainer"], + "registry": ["DockerRegistryContainer"], + "scylla": ["ScyllaContainer"], + "selenium": ["BrowserWebDriverContainer", "SeleniumVideoContainer", "get_image_name"], + "sftp": ["SFTPContainer", "SFTPUser"], + "trino": ["TrinoContainer"], + "valkey": ["ValkeyContainer"], + "vault": ["VaultContainer"], + "weaviate": ["WeaviateContainer"], +} + + +def _fresh_import(mod: str): + full_name = f"testcontainers.{mod}" + sys.modules.pop(full_name, None) + return importlib.import_module(full_name) + + +@pytest.mark.parametrize("mod,symbols", MODULES.items(), ids=MODULES.keys()) +def test_deprecation_warning(mod, symbols): + with pytest.warns(DeprecationWarning, match=f"testcontainers.community.{mod}"): + _fresh_import(mod) + + +@pytest.mark.parametrize("mod,symbols", MODULES.items(), ids=MODULES.keys()) +def test_symbols_exported(mod, symbols): + imported = _fresh_import(mod) + assert hasattr(imported, "__all__") + for symbol in symbols: + assert symbol in imported.__all__, f"{symbol} missing from testcontainers.{mod}.__all__" + assert hasattr(imported, symbol), f"{symbol} not accessible on testcontainers.{mod}" From de509145e4c5c0ad1cfdfc773b216b0e6a3f7e1e Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Sat, 6 Jun 2026 01:05:59 +0200 Subject: [PATCH 26/29] fix(ci): fix community test selection Make it based on python. Simplify the module names and add a test that enforces src, tests, pyproject.toml extra and docs are in sync. --- .github/workflows/ci-community.yml | 19 +---- .../community/{oracle-free.rst => oracle.rst} | 0 pyproject.toml | 2 +- scripts/compute_modules.py | 32 +++++++ .../community/influxdb/__init__.py | 85 ++----------------- src/testcontainers/community/influxdb/base.py | 84 ++++++++++++++++++ .../__init__.py => influxdb/version1.py} | 2 +- .../__init__.py => influxdb/version2.py} | 2 +- src/testcontainers/influxdb1.py | 2 +- src/testcontainers/influxdb2.py | 2 +- tests/community/influxdb/test_influxdb.py | 4 +- .../{oracle-free => oracle}/test_oracle.py | 0 tests/core/test_community_definition.py | 41 +++++++++ uv.lock | 14 +-- 14 files changed, 180 insertions(+), 109 deletions(-) rename docs/community/{oracle-free.rst => oracle.rst} (100%) create mode 100644 scripts/compute_modules.py create mode 100644 src/testcontainers/community/influxdb/base.py rename src/testcontainers/community/{influxdb1/__init__.py => influxdb/version1.py} (97%) rename src/testcontainers/community/{influxdb2/__init__.py => influxdb/version2.py} (98%) rename tests/community/{oracle-free => oracle}/test_oracle.py (100%) create mode 100644 tests/core/test_community_definition.py diff --git a/.github/workflows/ci-community.yml b/.github/workflows/ci-community.yml index a43a2661d..68d847f88 100644 --- a/.github/workflows/ci-community.yml +++ b/.github/workflows/ci-community.yml @@ -34,26 +34,11 @@ jobs: list-files: 'json' filters: | modules: - - 'src/testcontainers/community/**' - - 'tests/community/**' + - '**' - name: Compute modules from files id: compute-changes run: | - modules=$(echo '${{ toJson(steps.changed-files.outputs.modules_files) }}' | jq -c ' - [.[] | - if startswith("src/testcontainers/community/") then split("/")[3] - elif startswith("tests/community/") then split("/")[2] - else empty - end | - select(. and (startswith("__") | not)) | - if . == "oracle" then "oracle-free" - elif . == "influxdb1" or . == "influxdb2" then "influxdb" - else . - end - ] | unique - ') - echo "computed_modules=$modules" - echo "computed_modules=$modules" >> $GITHUB_OUTPUT + python3 scripts/compute_modules.py '${{ steps.changed-files.outputs.modules_files }}' outputs: changed_modules: ${{ steps.compute-changes.outputs.computed_modules }} diff --git a/docs/community/oracle-free.rst b/docs/community/oracle.rst similarity index 100% rename from docs/community/oracle-free.rst rename to docs/community/oracle.rst diff --git a/pyproject.toml b/pyproject.toml index c01cc96a8..a4220d2c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,7 +131,7 @@ test = [ "paramiko>=4", "twine>=6.2.0", "anyio>=4", - "tomli", # required for pyproject.toml tests, TODO: remove once we drop py3.10 support + "tomli>=2.0; python_version < '3.11'", # required for pyproject.toml tests, TODO: remove once we drop py3.10 support "pytest-xdist>=3.8.0", ] lint = [ diff --git a/scripts/compute_modules.py b/scripts/compute_modules.py new file mode 100644 index 000000000..5b338ef2d --- /dev/null +++ b/scripts/compute_modules.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +Extract the community module names out of lists of changed files. +""" + +import json +import os +import sys + + +def compute_modules(files: list[str]) -> list[str]: + modules = set() + for f in files: + if f.startswith("src/testcontainers/community/"): + part = f.split("/")[3] + elif f.startswith("tests/community/"): + part = f.split("/")[2] + else: + continue + if not part or part.startswith("__") or part.endswith(".md"): + continue + modules.add(part) + return sorted(modules) + + +if __name__ == "__main__": + files = json.loads(sys.argv[1]) + modules = compute_modules(files) + result = json.dumps(modules) + print(f"computed_modules={result}") # noqa: T201 + with open(os.environ["GITHUB_OUTPUT"], "a") as fh: + fh.write(f"computed_modules={result}\n") diff --git a/src/testcontainers/community/influxdb/__init__.py b/src/testcontainers/community/influxdb/__init__.py index 4c4ac46ec..7fe141137 100644 --- a/src/testcontainers/community/influxdb/__init__.py +++ b/src/testcontainers/community/influxdb/__init__.py @@ -10,16 +10,13 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - """ -testcontainers/influxdb provides means to spawn an InfluxDB instance within a Docker container. +testcontainers.community.influxdb provides means to spawn an InfluxDB instance within a Docker container. -- this influxdb.py module provides the common mechanism to spawn an InfluxDB container. +- The InfluxDbContainer provides the common mechanism to spawn an InfluxDB container. You are not likely to use this module directly. -- import the InfluxDb1Container class from the influxdb1/__init__.py module to spawn - a container for an InfluxDB 1.x instance -- import the InfluxDb2Container class from the influxdb2/__init__.py module to spawn - a container for an InfluxDB 2.x instance +- Import the InfluxDb1Container class to spawn a container for an InfluxDB 1.x instance +- Import the InfluxDb2Container class to spawn a container for an InfluxDB 2.x instance The 2 containers are separated in different modules for 2 reasons: - because the Docker images are not designed to be used in the same way @@ -27,74 +24,8 @@ so you won't have to install dependencies that you do not need """ -from typing import Optional - -from requests import get -from requests.exceptions import ConnectionError, ReadTimeout - -from testcontainers.core.container import DockerContainer -from testcontainers.core.waiting_utils import wait_container_is_ready - - -class InfluxDbContainer(DockerContainer): - """ - Abstract class for Docker containers of InfluxDB v1 and v2. - - Concrete implementations for InfluxDB 1.x and 2.x are separated iun different packages - because their respective clients rely on different Python libraries which we don't want - to import at the same time. - """ - - def __init__( - self, - # Docker image name - image: str, - # in the container, the default port for influxdb is often 8086 and not likely to change - container_port: int = 8086, - # specifies the port on the host machine where influxdb is exposed; a random available port otherwise - host_port: Optional[int] = None, - **docker_client_kw, - ) -> None: - super().__init__(image=image, **docker_client_kw) - self.container_port = container_port - self.host_port = host_port - self.with_bind_ports(self.container_port, self.host_port) - - def get_url(self) -> str: - """ - Returns the url to interact with the InfluxDB container (health check, REST API, etc.) - """ - host = self.get_container_host_ip() - port = self.get_exposed_port(self.container_port) - - return f"http://{host}:{port}" - - @wait_container_is_ready(ConnectionError, ReadTimeout) - def _health_check(self) -> dict: - """ - Performs a health check on the running InfluxDB container. - The call is retried until it works thanks to the @wait_container_is_ready decorator. - See its documentation for the max number of retries or the timeout. - """ - - url = self.get_url() - response = get(f"{url}/health", timeout=1) - response.raise_for_status() - - return response.json() - - def get_influxdb_version(self) -> str: - """ - Returns the version of the InfluxDB service, as returned by the healthcheck. - """ - - return self._health_check().get("version") - - def start(self) -> "InfluxDbContainer": - """ - Spawns a container of the InfluxDB Docker image, ready to be used. - """ - super().start() - self._health_check() +from .base import InfluxDbContainer +from .version1 import InfluxDb1Container +from .version2 import InfluxDb2Container - return self +__all__ = ["InfluxDb1Container", "InfluxDb2Container", "InfluxDbContainer"] diff --git a/src/testcontainers/community/influxdb/base.py b/src/testcontainers/community/influxdb/base.py new file mode 100644 index 000000000..5636dd985 --- /dev/null +++ b/src/testcontainers/community/influxdb/base.py @@ -0,0 +1,84 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from typing import Optional + +from requests import get +from requests.exceptions import ConnectionError, ReadTimeout + +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_container_is_ready + + +class InfluxDbContainer(DockerContainer): + """ + Abstract class for Docker containers of InfluxDB v1 and v2. + + Concrete implementations for InfluxDB 1.x and 2.x are separated iun different packages + because their respective clients rely on different Python libraries which we don't want + to import at the same time. + """ + + def __init__( + self, + # Docker image name + image: str, + # in the container, the default port for influxdb is often 8086 and not likely to change + container_port: int = 8086, + # specifies the port on the host machine where influxdb is exposed; a random available port otherwise + host_port: Optional[int] = None, + **docker_client_kw, + ) -> None: + super().__init__(image=image, **docker_client_kw) + self.container_port = container_port + self.host_port = host_port + self.with_bind_ports(self.container_port, self.host_port) + + def get_url(self) -> str: + """ + Returns the url to interact with the InfluxDB container (health check, REST API, etc.) + """ + host = self.get_container_host_ip() + port = self.get_exposed_port(self.container_port) + + return f"http://{host}:{port}" + + @wait_container_is_ready(ConnectionError, ReadTimeout) + def _health_check(self) -> dict: + """ + Performs a health check on the running InfluxDB container. + The call is retried until it works thanks to the @wait_container_is_ready decorator. + See its documentation for the max number of retries or the timeout. + """ + + url = self.get_url() + response = get(f"{url}/health", timeout=1) + response.raise_for_status() + + return response.json() + + def get_influxdb_version(self) -> str: + """ + Returns the version of the InfluxDB service, as returned by the healthcheck. + """ + + return self._health_check().get("version") + + def start(self) -> "InfluxDbContainer": + """ + Spawns a container of the InfluxDB Docker image, ready to be used. + """ + super().start() + self._health_check() + + return self diff --git a/src/testcontainers/community/influxdb1/__init__.py b/src/testcontainers/community/influxdb/version1.py similarity index 97% rename from src/testcontainers/community/influxdb1/__init__.py rename to src/testcontainers/community/influxdb/version1.py index 8cabf2bc6..7461eb362 100644 --- a/src/testcontainers/community/influxdb1/__init__.py +++ b/src/testcontainers/community/influxdb/version1.py @@ -15,7 +15,7 @@ from influxdb import InfluxDBClient -from testcontainers.community.influxdb import InfluxDbContainer +from .base import InfluxDbContainer class InfluxDb1Container(InfluxDbContainer): diff --git a/src/testcontainers/community/influxdb2/__init__.py b/src/testcontainers/community/influxdb/version2.py similarity index 98% rename from src/testcontainers/community/influxdb2/__init__.py rename to src/testcontainers/community/influxdb/version2.py index 6226e8c43..0d10617d7 100644 --- a/src/testcontainers/community/influxdb2/__init__.py +++ b/src/testcontainers/community/influxdb/version2.py @@ -16,7 +16,7 @@ from influxdb_client import InfluxDBClient, Organization -from testcontainers.community.influxdb import InfluxDbContainer +from .base import InfluxDbContainer class InfluxDb2Container(InfluxDbContainer): diff --git a/src/testcontainers/influxdb1.py b/src/testcontainers/influxdb1.py index 11a486628..aa84ba58a 100644 --- a/src/testcontainers/influxdb1.py +++ b/src/testcontainers/influxdb1.py @@ -1,6 +1,6 @@ import warnings -from testcontainers.community.influxdb1 import ( +from testcontainers.community.influxdb import ( InfluxDb1Container, ) diff --git a/src/testcontainers/influxdb2.py b/src/testcontainers/influxdb2.py index e9efb6765..293da1fa3 100644 --- a/src/testcontainers/influxdb2.py +++ b/src/testcontainers/influxdb2.py @@ -1,6 +1,6 @@ import warnings -from testcontainers.community.influxdb2 import ( +from testcontainers.community.influxdb import ( InfluxDb2Container, ) diff --git a/tests/community/influxdb/test_influxdb.py b/tests/community/influxdb/test_influxdb.py index 357d23df3..cecbbc98e 100644 --- a/tests/community/influxdb/test_influxdb.py +++ b/tests/community/influxdb/test_influxdb.py @@ -18,9 +18,7 @@ from influxdb_client import Bucket from influxdb_client.client.write_api import SYNCHRONOUS -from testcontainers.community.influxdb import InfluxDbContainer -from testcontainers.community.influxdb1 import InfluxDb1Container -from testcontainers.community.influxdb2 import InfluxDb2Container +from testcontainers.community.influxdb import InfluxDb1Container, InfluxDb2Container, InfluxDbContainer @pytest.mark.parametrize( diff --git a/tests/community/oracle-free/test_oracle.py b/tests/community/oracle/test_oracle.py similarity index 100% rename from tests/community/oracle-free/test_oracle.py rename to tests/community/oracle/test_oracle.py diff --git a/tests/core/test_community_definition.py b/tests/core/test_community_definition.py new file mode 100644 index 000000000..2f1138f02 --- /dev/null +++ b/tests/core/test_community_definition.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from pathlib import Path + +import pytest + +try: + import tomllib # type: ignore[import-not-found] +except ImportError: + import tomli as tomllib # type: ignore[no-retyping, unused-ignore] + + +BASE_DIR = Path(__file__).parent.parent.parent.resolve() +PYPROJECT_TOML = BASE_DIR / "pyproject.toml" +SRC_DIR = BASE_DIR / "src" / "testcontainers" / "community" +DOCS_DIR = BASE_DIR / "docs" / "community" +TESTS_DIR = BASE_DIR / "tests" / "community" + +SRC_MODULES = frozenset({f.stem for f in SRC_DIR.iterdir() if f.is_dir()}) +DOCS_MODULES = frozenset({f.stem for f in DOCS_DIR.glob("*.rst")}) +TESTS_MODULES = frozenset({f.stem for f in TESTS_DIR.iterdir() if f.is_dir()}) + +ALL_MODULES = frozenset((SRC_MODULES | DOCS_MODULES | TESTS_MODULES) - {"__init__", "__pycache__"}) + + +@pytest.fixture(scope="session") +def pyproject_toml_extra() -> frozenset[str]: + with PYPROJECT_TOML.open("rb") as f: + pyproject = tomllib.load(f) + return frozenset(pyproject["project"]["optional-dependencies"].keys()) + + +@pytest.mark.parametrize("module", sorted(ALL_MODULES)) +def test_community_definition(module: str, pyproject_toml_extra: frozenset[str]) -> None: + """ + Check that community modules defined in docs, src, tests and pyproject.toml extras are in sync + """ + assert module in SRC_MODULES, f"{module} is missing in src/testcontainers/community" + assert module in DOCS_MODULES, f"{module} is missing in docs/community" + assert module in TESTS_MODULES, f"{module} is missing in tests/community" + assert module in pyproject_toml_extra, f"{module} is missing in pyproject.toml extras" diff --git a/uv.lock b/uv.lock index e01ce500f..53ecd067a 100644 --- a/uv.lock +++ b/uv.lock @@ -501,16 +501,16 @@ wheels = [ [[package]] name = "boto3" -version = "1.43.23" +version = "1.43.21" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/7e/18f6d87625930231708681ccfba20c2c6ade8d977c37d388992c0589efdd/boto3-1.43.23.tar.gz", hash = "sha256:5d26498702ffd021dc0d57d0eefcc7101cd995ea0ed08c057c9b631efccbaa48", size = 113242, upload-time = "2026-06-04T19:39:37.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/f2/0ef88b6584285002a8a8000e34340f56e82681ad2b8a76ea8bd3ecaa5cb9/boto3-1.43.21.tar.gz", hash = "sha256:6dfeb70bf4f9a3514b91c7199f475f71f939199d62f9c63cd555b033fb283f89", size = 113157, upload-time = "2026-06-03T07:09:23.263Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/1e/27b67ee4d10cc755aa9a35d45aa476d7bb2366c4dea91b0db94fa0fe27bd/boto3-1.43.23-py3-none-any.whl", hash = "sha256:8afc058924ef8a5c62467fe2e1e2e0304c22018587a044714da89f9c602ba856", size = 140536, upload-time = "2026-06-04T19:39:34.657Z" }, + { url = "https://files.pythonhosted.org/packages/01/ea/5352950cbee9d1e8392e5396ddbc6defc982414e2abc004b501139bce13c/boto3-1.43.21-py3-none-any.whl", hash = "sha256:8bb863b32dabe5baa4f2e3701778c259243ba117e4811a595411819958c4fb1b", size = 140534, upload-time = "2026-06-03T07:09:21.18Z" }, ] [[package]] @@ -5554,7 +5554,7 @@ dev = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sqlalchemy" }, { name = "sqlalchemy-cockroachdb" }, - { name = "tomli" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "twine" }, { name = "types-docker" }, { name = "types-paramiko" }, @@ -5595,7 +5595,7 @@ test = [ { name = "pytest-xdist" }, { name = "sqlalchemy" }, { name = "sqlalchemy-cockroachdb" }, - { name = "tomli" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "twine" }, ] @@ -5684,7 +5684,7 @@ dev = [ { name = "sphinx", marker = "python_full_version >= '3.11'", specifier = ">=9" }, { name = "sqlalchemy", specifier = ">=2" }, { name = "sqlalchemy-cockroachdb", specifier = ">=2" }, - { name = "tomli" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, { name = "twine", specifier = ">=6.2.0" }, { name = "types-docker", specifier = ">=7.1.0.20260518" }, { name = "types-paramiko", specifier = ">=4" }, @@ -5724,7 +5724,7 @@ test = [ { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "sqlalchemy", specifier = ">=2" }, { name = "sqlalchemy-cockroachdb", specifier = ">=2" }, - { name = "tomli" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, { name = "twine", specifier = ">=6.2.0" }, ] From 23e57eaa274297b888bfde31d362c8c10d663ff7 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Sat, 6 Jun 2026 01:24:39 +0200 Subject: [PATCH 27/29] fix(ci): correct coverage paths --- pyproject.toml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a4220d2c3..2e3864585 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -210,12 +210,22 @@ exclude_lines = [ ] [tool.coverage.paths] -source = [ +# only consider non-community/core packages for the coverage. +# Community packages are run conditionally anyway. +core = [ "src/testcontainers/core", + "*/site-packages/testcontainers/core", + "*/dist-packages/testcontainers/core", +] +compose = [ "src/testcontainers/compose", + "*/site-packages/testcontainers/compose", + "*/dist-packages/testcontainers/compose", +] +concat = [ "src/testcontainers/cocat", - "*/site-packages/testcontainers", - "*/dist-packages/testcontainers", + "*/site-packages/testcontainer/cocat", + "*/dist-packages/testcontainers/cocat", ] [tool.ruff] From b19833b30b2b382d4f6e87cad6fb305132f63eab Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Sat, 6 Jun 2026 01:35:44 +0200 Subject: [PATCH 28/29] fix(ci): ignore TYPE_CHECKING block in coverage --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e3864585..c7352b6f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -204,9 +204,10 @@ branch = true omit = ["oracle.py"] [tool.coverage.report] -exclude_lines = [ +exclude_also = [ "pass", "raise NotImplementedError", + "if TYPE_CHECKING:", ] [tool.coverage.paths] From ca0c8efb0d7a66ffd389e7c001dac3a14461b3f2 Mon Sep 17 00:00:00 2001 From: Carli* Freudenberg Date: Sat, 6 Jun 2026 01:39:35 +0200 Subject: [PATCH 29/29] fix(ci): ignore @overload in coverage --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c7352b6f8..7a4797077 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,6 +208,7 @@ exclude_also = [ "pass", "raise NotImplementedError", "if TYPE_CHECKING:", + "@overload", ] [tool.coverage.paths]