diff --git a/.github/settings.yml b/.github/settings.yml index e72584e6f..122fd660d 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -63,6 +63,7 @@ labels: - { name: '🐧 linux', color: '#3ED4D',, description: '' } - { name: '👀 requires attention', color: '#fef2c0', description: '' } - { name: '📖 documentation', color: '#d93f0b', description: '' } + - { name: '📦 package: cassandra', color: '#0052CC', description: '' } - { name: '📦 package: clickhouse', color: '#0052CC', description: '' } - { name: '📦 package: compose', color: '#0052CC', description: '' } - { name: '📦 package: core', color: '#0052CC', description: '' } diff --git a/index.rst b/index.rst index d9d4a010a..90cf7ba6f 100644 --- a/index.rst +++ b/index.rst @@ -17,6 +17,7 @@ testcontainers-python facilitates the use of Docker containers for functional an core/README modules/arangodb/README modules/azurite/README + modules/cassandra/README modules/chroma/README modules/clickhouse/README modules/elasticsearch/README diff --git a/modules/cassandra/README.rst b/modules/cassandra/README.rst new file mode 100644 index 000000000..44216d6be --- /dev/null +++ b/modules/cassandra/README.rst @@ -0,0 +1,2 @@ +.. autoclass:: testcontainers.cassandra.CassandraContainer +.. title:: testcontainers.cassandra.CassandraContainer diff --git a/modules/cassandra/testcontainers/cassandra/__init__.py b/modules/cassandra/testcontainers/cassandra/__init__.py new file mode 100644 index 000000000..4e6618b7b --- /dev/null +++ b/modules/cassandra/testcontainers/cassandra/__init__.py @@ -0,0 +1,62 @@ +# +# 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 testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + + +class CassandraContainer(DockerContainer): + """ + Cassandra database container. + + Example: + + .. doctest:: + + >>> from testcontainers.cassandra import CassandraContainer + >>> from cassandra.cluster import Cluster, DCAwareRoundRobinPolicy + + >>> with CassandraContainer("cassandra:4.1.4") as cassandra, Cluster( + ... cassandra.get_contact_points(), + ... load_balancing_policy=DCAwareRoundRobinPolicy(cassandra.get_local_datacenter()), + ... ) as cluster: + ... session = cluster.connect() + ... result = session.execute("SELECT release_version FROM system.local;") + ... result.one().release_version + '4.1.4' + """ + + CQL_PORT = 9042 + DEFAULT_LOCAL_DATACENTER = "datacenter1" + + def __init__(self, image: str = "cassandra:latest", **kwargs) -> None: + super().__init__(image=image, **kwargs) + self.with_exposed_ports(self.CQL_PORT) + self.with_env("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0") + self.with_env("HEAP_NEWSIZE", "128M") + self.with_env("MAX_HEAP_SIZE", "1024M") + self.with_env("CASSANDRA_ENDPOINT_SNITCH", "GossipingPropertyFileSnitch") + self.with_env("CASSANDRA_DC", self.DEFAULT_LOCAL_DATACENTER) + + def _connect(self): + wait_for_logs(self, "Startup complete") + + def start(self) -> "CassandraContainer": + super().start() + self._connect() + return self + + def get_contact_points(self) -> list[tuple[str, int]]: + return [(self.get_container_host_ip(), int(self.get_exposed_port(self.CQL_PORT)))] + + def get_local_datacenter(self) -> str: + return self.env.get("CASSANDRA_DC", self.DEFAULT_LOCAL_DATACENTER) diff --git a/modules/cassandra/tests/test_cassandra.py b/modules/cassandra/tests/test_cassandra.py new file mode 100644 index 000000000..1aa5858b7 --- /dev/null +++ b/modules/cassandra/tests/test_cassandra.py @@ -0,0 +1,14 @@ +from cassandra.cluster import Cluster, DCAwareRoundRobinPolicy + +from testcontainers.cassandra import CassandraContainer + + +def test_docker_run_cassandra(): + with CassandraContainer("cassandra:4.1.4") as cassandra: + cluster = Cluster( + cassandra.get_contact_points(), + load_balancing_policy=DCAwareRoundRobinPolicy(cassandra.get_local_datacenter()), + ) + session = cluster.connect() + result = session.execute("SELECT release_version FROM system.local;") + assert result.one().release_version == "4.1.4" diff --git a/poetry.lock b/poetry.lock index a3f795aae..f05b8b4b5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -272,6 +272,53 @@ files = [ {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, ] +[[package]] +name = "cassandra-driver" +version = "3.29.1" +description = "DataStax Driver for Apache Cassandra" +optional = false +python-versions = "*" +files = [ + {file = "cassandra-driver-3.29.1.tar.gz", hash = "sha256:38e9c2a2f2a9664bb03f1f852d5fccaeff2163942b5db35dffcf8bf32a51cfe5"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8f175c7616a63ca48cb8bd4acc443e2a3d889964d5157cead761f23cc8db7bd"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d66398952b9cd21c40edff56e22b6d3bce765edc94b207ddb5896e7bc9aa088"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbc6f575ef109ce5d4abfa2033bf36c394032abd83e32ab671159ce68e7e17b"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78f241af75696adb3e470209e2fbb498804c99e2b197d24d74774eee6784f283"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-win32.whl", hash = "sha256:54d9e651a742d6ca3d874ef8d06a40fa032d2dba97142da2d36f60c5675e39f8"}, + {file = "cassandra_driver-3.29.1-cp310-cp310-win_amd64.whl", hash = "sha256:630dc5423cd40eba0ee9db31065e2238098ff1a25a6b1bd36360f85738f26e4b"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b841d38c96bb878d31df393954863652d6d3a85f47bcc00fd1d70a5ea73023f"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19cc7375f673e215bd4cbbefae2de9f07830be7dabef55284a2d2ff8d8691efe"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b74b355be3dcafe652fffda8f14f385ccc1a8dae9df28e6080cc660da39b45f"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e6dac7eddd3f4581859f180383574068a3f113907811b4dad755a8ace4c3fbd"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-win32.whl", hash = "sha256:293a79dba417112b56320ed0013d71fd7520f5fc4a5fd2ac8000c762c6dd5b07"}, + {file = "cassandra_driver-3.29.1-cp311-cp311-win_amd64.whl", hash = "sha256:7c2374fdf1099047a6c9c8329c79d71ad11e61d9cca7de92a0f49655da4bdd8a"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4431a0c836f33a33c733c84997fbdb6398be005c4d18a8c8525c469fdc29393c"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23b08381b171a9e42ace483a82457edcddada9e8367e31677b97538cde2dc34"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4beb29a0139e63a10a5b9a3c7b72c30a4e6e20c9f0574f9d22c0d4144fe3d348"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b206423cc454a78f16b411e7cb641dddc26168ac2e18f2c13665f5f3c89868c"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-win32.whl", hash = "sha256:ac898cca7303a3a2a3070513eee12ef0f1be1a0796935c5b8aa13dae8c0a7f7e"}, + {file = "cassandra_driver-3.29.1-cp312-cp312-win_amd64.whl", hash = "sha256:4ad0c9fb2229048ad6ff8c6ddbf1fdc78b111f2b061c66237c2257fcc4a31b14"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4282c5deac462e4bb0f6fd0553a33d514dbd5ee99d0812594210080330ddd1a2"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:41ca7eea069754002418d3bdfbd3dfd150ea12cb9db474ab1a01fa4679a05bcb"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6639ccb268c4dc754bc45e03551711780d0e02cb298ab26cde1f42b7bcc74f8"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a9d7d3b1be24a7f113b5404186ccccc977520401303a8fe78ba34134cad2482"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-win32.whl", hash = "sha256:81c8fd556c6e1bb93577e69c1f10a3fadf7ddb93958d226ccbb72389396e9a92"}, + {file = "cassandra_driver-3.29.1-cp38-cp38-win_amd64.whl", hash = "sha256:cfe70ed0f27af949de2767ea9cef4092584e8748759374a55bf23c30746c7b23"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2c03c1d834ac1a0ae39f9af297a8cd38829003ce910b08b324fb3abe488ce2b"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9a3e1e2b01f3b7a5cf75c97401bce830071d99c42464352087d7475e0161af93"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90c42006665a4e490b0766b70f3d637f36a30accbef2da35d6d4081c0e0bafc3"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c1aca41f45772f9759e8246030907d92bc35fbbdc91525a3cb9b49939b80ad7"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-win32.whl", hash = "sha256:ce4a66245d4a0c8b07fdcb6398698c2c42eb71245fb49cff39435bb702ff7be6"}, + {file = "cassandra_driver-3.29.1-cp39-cp39-win_amd64.whl", hash = "sha256:4cae69ceb1b1d9383e988a1b790115253eacf7867ceb15ed2adb736e3ce981be"}, +] + +[package.dependencies] +geomet = ">=0.1,<0.3" + +[package.extras] +cle = ["cryptography (>=35.0)"] +graph = ["gremlinpython (==3.4.6)"] + [[package]] name = "certifi" version = "2024.2.2" @@ -482,6 +529,20 @@ requests = ">=2.28" tenacity = ">=8.2.3" typing-extensions = ">=4.5.0" +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "clickhouse-driver" version = "0.2.7" @@ -885,6 +946,21 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "geomet" +version = "0.2.1.post1" +description = "GeoJSON <-> WKT/WKB conversion utilities" +optional = false +python-versions = ">2.6, !=3.3.*, <4" +files = [ + {file = "geomet-0.2.1.post1-py3-none-any.whl", hash = "sha256:a41a1e336b381416d6cbed7f1745c848e91defaa4d4c1bdc1312732e46ffad2b"}, + {file = "geomet-0.2.1.post1.tar.gz", hash = "sha256:91d754f7c298cbfcabd3befdb69c641c27fe75e808b27aa55028605761d17e95"}, +] + +[package.dependencies] +click = "*" +six = "*" + [[package]] name = "google-api-core" version = "2.17.1" @@ -3909,6 +3985,7 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [extras] arangodb = ["python-arango"] azurite = ["azure-storage-blob"] +cassandra = [] chroma = ["chromadb-client"] clickhouse = ["clickhouse-driver"] elasticsearch = [] @@ -3935,4 +4012,4 @@ weaviate = ["weaviate-client"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "173a16b21517cede7cb30c8d67444bd179ed719015f78649f574e564c253ee81" +content-hash = "c5659a08d1acbd86baa459df9d85b647e6611c546b955a702242558ca0fa0c9d" diff --git a/pyproject.toml b/pyproject.toml index dff8fd382..c6a691c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ packages = [ { include = "testcontainers", from = "core" }, { include = "testcontainers", from = "modules/arangodb" }, { include = "testcontainers", from = "modules/azurite" }, + { include = "testcontainers", from = "modules/cassandra" }, { include = "testcontainers", from = "modules/chroma" }, { include = "testcontainers", from = "modules/clickhouse" }, { include = "testcontainers", from = "modules/elasticsearch" }, @@ -93,6 +94,7 @@ chromadb-client = { version = "*", optional = true } [tool.poetry.extras] arangodb = ["python-arango"] azurite = ["azure-storage-blob"] +cassandra = ["cassandra-driver"] clickhouse = ["clickhouse-driver"] elasticsearch = [] google = ["google-cloud-pubsub", "google-cloud-datastore"] @@ -130,6 +132,7 @@ pg8000 = "*" sqlalchemy = "*" psycopg = "*" kafka-python = "^2.0.2" +cassandra-driver = "*" [[tool.poetry.source]] name = "PyPI" @@ -228,6 +231,7 @@ mypy_path = [ "core", # "modules/arangodb", # "modules/azurite", +# "modules/cassandra", # "modules/clickhouse", # "modules/elasticsearch", # "modules/google",