Skip to content

Commit 2401da2

Browse files
committed
Remove SqlAlchemy dependency from postgres container
Create a new generic database subclass - DependencyFreeDbContainer Remove test that was testing sqlalchemy support for driver types Add tests for supported versions of Postgres
1 parent efec6cb commit 2401da2

File tree

3 files changed

+37
-41
lines changed

3 files changed

+37
-41
lines changed

core/testcontainers/core/generic.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@
2525
pass
2626

2727

28+
class DependencyFreeDbContainer(DockerContainer):
29+
"""
30+
A generic database without any package dependencies
31+
"""
32+
def start(self) -> 'DbContainer':
33+
self._configure()
34+
super().start()
35+
self._verify_connection()
36+
return self
37+
38+
def _verify_status(self) -> 'DependencyFreeDbContainer':
39+
"""override this method to ensure the database is running and accepting connections"""
40+
raise NotImplementedError
41+
42+
def _configure(self) -> None:
43+
raise NotImplementedError
44+
45+
2846
class DbContainer(DockerContainer):
2947
"""
3048
Generic database container.

postgres/testcontainers/postgres/__init__.py

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@
1111
# License for the specific language governing permissions and limitations
1212
# under the License.
1313
import os
14-
import re
15-
from datetime import UTC, datetime
1614
from time import sleep
1715
from typing import Optional
1816

1917
from testcontainers.core.config import MAX_TRIES, SLEEP_TIME
20-
from testcontainers.core.generic import DbContainer
18+
from testcontainers.core.generic import DependencyFreeDbContainer
2119
from testcontainers.core.utils import raise_for_deprecated_parameter
22-
from testcontainers.core.waiting_utils import wait_container_is_ready
20+
from testcontainers.core.waiting_utils import (wait_container_is_ready,
21+
wait_for_logs)
2322

2423

25-
class PostgresContainer(DbContainer):
24+
class PostgresContainer(DependencyFreeDbContainer):
2625
"""
2726
Postgres database container.
2827
@@ -63,33 +62,17 @@ def _configure(self) -> None:
6362
self.with_env("POSTGRES_PASSWORD", self.password)
6463
self.with_env("POSTGRES_DB", self.dbname)
6564

66-
def get_connection_url(self, host=None) -> str:
67-
return super()._create_connection_url(
68-
dialect=f"postgresql+{self.driver}", username=self.username,
69-
password=self.password, dbname=self.dbname, host=host,
70-
port=self.port,
71-
)
72-
7365
@wait_container_is_ready()
74-
def _connect(self) -> None:
75-
count = 0
66+
def _verify_status(self) -> None:
67+
wait_for_logs(self, ".*database system is ready to accept connections.*", MAX_TRIES, SLEEP_TIME)
7668

77-
# ALTERNATE IMPLEMENTATION based on comments from @HofmeisterAn
78-
# expr = re.compile(r'(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z).*')
79-
# timestamp = datetime.now()
69+
count = 0
8070
while count < MAX_TRIES:
8171
status, _ = self.exec(f"pg_isready -hlocalhost -p{self.port} -U{self.username}")
8272
if status == 0:
8373
return
8474

85-
# ALTERNATE IMPLEMENTATION based on comments from @HofmeisterAn
86-
# stdout = self._container.logs(stderr = False, timestamps = True, since = timestamp)
87-
# lines = stdout.decode("utf-8").split("\n")
88-
# for line in lines:
89-
# if m:= re.match(expr, line):
90-
# timestamp = datetime.fromisoformat(m.groupdict()["ts"]).replace(tzinfo=None)
91-
# if "database system is ready to accept connections" in line:
92-
# return
93-
9475
sleep(SLEEP_TIME)
95-
count += 1
76+
count += 1
77+
78+
raise RuntimeError("Postgres could not get into a ready state")

postgres/tests/test_postgres.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
1-
import sqlalchemy
21
from testcontainers.postgres import PostgresContainer
32

43

54
def test_docker_run_postgres():
6-
postgres_container = PostgresContainer("postgres:9.5")
7-
with postgres_container as postgres:
8-
engine = sqlalchemy.create_engine(postgres.get_connection_url())
9-
with engine.begin() as connection:
10-
result = connection.execute(sqlalchemy.text("select version()"))
11-
for row in result:
12-
assert row[0].lower().startswith("postgresql 9.5")
5+
# https://www.postgresql.org/support/versioning/
6+
supported_versions = ["11", "12", "13", "14", "latest"]
137

8+
for version in supported_versions:
9+
postgres_container = PostgresContainer(f"postgres:{version}")
10+
with postgres_container as postgres:
11+
status, msg = postgres.exec(f"pg_isready -hlocalhost -p{postgres.port} -U{postgres.username}")
12+
13+
assert msg.decode("utf-8").endswith("accepting connections\n")
14+
assert status == 0
1415

15-
def test_docker_run_postgres_with_driver_pg8000():
16-
postgres_container = PostgresContainer("postgres:9.5", driver="pg8000")
17-
with postgres_container as postgres:
18-
engine = sqlalchemy.create_engine(postgres.get_connection_url())
19-
with engine.begin() as connection:
20-
connection.execute(sqlalchemy.text("select 1=1"))

0 commit comments

Comments
 (0)