testcontainers/testcontainers-python

Bug: Postgres Port not working

Biscgit opened this issue · 5 comments

Describe the bug

Creating a PostgresContainer gets stuck on creating when port= is defined and raises a Runtime error after 1-2 minutes except when setting to the default Postgres port 5432.
Not defining it leads to random port mapping (as intended), but reading it with PostgresContainer.port returns the default Postgres port instead of the randomly selected one. It gets correctly mapped in the connection url though.

To Reproduce

import asyncpg
import pytest
from testcontainers.postgres import PostgresContainer

pytest_plugins = ('pytest_asyncio',)


# Gets stuck in creating container process and eventually raises a RuntimeException
@pytest.mark.asyncio
async def test_postgres_start():
    port = 3182
    with PostgresContainer("postgres:16-alpine", port=port) as pg:
        pass


# Reading port via .port returns the default port and not the actual one
@pytest.mark.asyncio
async def test_postgres_port():
    # Gets stuck in creating process:
    with PostgresContainer("postgres:16-alpine") as pg:
        port = pg.get_connection_url().split(":")[-1].split('/')[0]
        assert int(port) == pg.port

You can check the port with nmap as-well to be certain that the variable fails.
Setting driver=None leads to the same results.

Runtime environment

Versions and specs (at time of creating latest stable versions):

  • Machine: EndeavourOS Linux 6.8.9-arch1-2 x86_64 GNU/Linux
  • Docker: v26.1.2
  • Python: v3.12.3
    • pip: v24.0
    • pytest: v8.2.0
    • pytest-asyncio: v0.23.6
    • asyncpg: v0.29.0
    • testcontainers: v4.4.0

Hello!
Would it be possible to maybe expand the documentation on what exactly port= does? Because neither in the Python documentation nor in Postgres-Tests was I able to find any hints.
Or implementing a simple function for getting the containers port directly.

you can get the exposed port using this function: https://github.com/testcontainers/testcontainers-python/blob/main/core/testcontainers/core/container.py#L140

@wait_container_is_ready()
def get_exposed_port(self, port: int) -> str:
mapped_port = self.get_docker_client().port(self._container.id, port)
if inside_container():
gateway_ip = self.get_docker_client().gateway_ip(self._container.id)
host = self.get_docker_client().host()
if gateway_ip == host:
return port
return mapped_port

e.g. by:

mkdir tcp-postgres-port-demo
cd tcp-postgres-port-demo
python -m venv .venv
test -d .venv/bin && . .venv/bin/activate
test -d .venv/Scripts && . .venv/Scripts/activate
pip install testcontainers pytest
cat > example.py <<EOF
from testcontainers.postgres import PostgresContainer
def test():
    with PostgresContainer("postgres:16-alpine") as pg:
        print(pg.get_exposed_port(5432))
EOF
pytest -s example.py

documentation can be improved a lot, you are right about that

okay, that works too, thanks 👍