diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 956a3f18..aff59631 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,13 +25,6 @@ jobs: restore-keys: | ${{ runner.os }}-pip- ${{ runner.os }}- - - name: Install system requirements - run: | - sudo apt-get install -y --no-install-recommends unixodbc-dev # required for pyodbc - curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - curl https://packages.microsoft.com/config/ubuntu/18.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list - sudo apt-get -qq update - sudo ACCEPT_EULA=Y apt-get -y install msodbcsql17 - name: Install Python dependencies run: | python -m pip install --upgrade pip @@ -51,5 +44,5 @@ jobs: run: | flake8 sphinx-build -nW docs docs/_build/html - py.test -sv --cov-config .coveragerc --cov-report html:skip-covered --cov-report term:skip-covered --cov=testcontainers --tb=short tests/ + py.test -svx --cov-config .coveragerc --cov-report html:skip-covered --cov-report term:skip-covered --cov=testcontainers --tb=short tests/ codecov diff --git a/Dockerfile b/Dockerfile index ef3cb765..f839d68a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,12 @@ ARG version=3.8 FROM python:${version} +WORKDIR /workspace +RUN pip install --upgrade pip \ + && apt-get update \ + && apt-get install -y \ + freetds-dev \ + && rm -rf /var/lib/apt/lists/* WORKDIR /workspace ARG version=3.8 COPY requirements/${version}.txt requirements.txt diff --git a/Makefile b/Makefile index 93ad8bed..032dc90c 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ -PYTHON_VERSIONS = 3.6 3.7 3.8 +PYTHON_VERSIONS = 3.6 3.7 3.8 3.9 REQUIREMENTS = $(addprefix requirements/,${PYTHON_VERSIONS:=.txt}) TESTS = $(addprefix tests/,${PYTHON_VERSIONS}) IMAGES = $(addprefix image/,${PYTHON_VERSIONS}) RUN = docker run --rm -it + .PHONY : docs # Default target @@ -17,7 +18,7 @@ requirements : ${REQUIREMENTS} ${REQUIREMENTS} : requirements/%.txt : requirements.in setup.py mkdir -p $(dir $@) ${RUN} -w /workspace -v `pwd`:/workspace python:$* bash -c \ - "pip install pip-tools && pip-compile -v -o $@ $<" + "pip install pip-tools && pip-compile -v --upgrade -o $@ $<" # Targets to build docker images @@ -33,8 +34,8 @@ ${IMAGES} : image/% : requirements/%.txt tests : ${TESTS} ${TESTS} : tests/% : image/% - ${RUN} -v /var/run/docker.sock:/var/run/docker.sock testcontainers-python:$* bash -c \ - "flake8 && pytest -v ${ARGS}" + ${RUN} -v /var/run/docker.sock:/var/run/docker.sock testcontainers-python:$* \ + bash -c "flake8 && pytest -v ${ARGS}" # Target to build the documentation diff --git a/requirements.in b/requirements.in index c84b9a3e..ec56a5d6 100644 --- a/requirements.in +++ b/requirements.in @@ -1,7 +1,6 @@ -e file:.[docker-compose,mysql,oracle,postgresql,selenium,google-cloud-pubsub,mongo,redis,mssqlserver,neo4j,kafka,rabbitmq] codecov>=2.1.0 -flake8 +flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages. pytest pytest-cov sphinx -docker<4.3.0 diff --git a/requirements/3.6.txt b/requirements/3.6.txt index 37a8f65e..ad694224 100644 --- a/requirements/3.6.txt +++ b/requirements/3.6.txt @@ -8,117 +8,130 @@ # via -r requirements.in alabaster==0.7.12 # via sphinx -attrs==19.3.0 +async-timeout==4.0.2 + # via redis +attrs==21.4.0 # via # jsonschema # pytest -babel==2.8.0 +babel==2.9.1 # via sphinx bcrypt==3.2.0 # via paramiko -cached-property==1.5.1 +cached-property==1.5.2 # via docker-compose -cachetools==4.1.1 +cachetools==4.2.4 # via google-auth -certifi==2020.6.20 +certifi==2021.10.8 # via requests -cffi==1.14.2 +cffi==1.15.0 # via # bcrypt # cryptography # pynacl -chardet==3.0.4 +charset-normalizer==2.0.12 # via requests -codecov==2.1.8 +codecov==2.1.12 # via -r requirements.in -coverage==5.2.1 +coverage[toml]==6.2 # via # codecov # pytest-cov -cryptography==3.0 +cryptography==36.0.2 # via paramiko -cx-oracle==8.0.0 +cx-oracle==8.3.0 # via testcontainers +deprecated==1.2.13 + # via redis deprecation==2.1.0 # via testcontainers -distro==1.5.0 +distro==1.7.0 # via docker-compose -docker[ssh]==4.2.2 +docker[ssh]==5.0.3 # via - # -r requirements.in # docker-compose # testcontainers -docker-compose==1.26.2 +docker-compose==1.29.2 # via testcontainers dockerpty==0.4.1 # via docker-compose docopt==0.6.2 # via docker-compose -docutils==0.16 +docutils==0.17.1 # via sphinx -flake8==3.8.3 +entrypoints==0.3 + # via flake8 +flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==1.22.1 +google-api-core[grpc]==2.7.1 # via google-cloud-pubsub -google-auth==1.20.1 +google-auth==2.6.2 # via google-api-core google-cloud-pubsub==1.7.0 # via testcontainers -googleapis-common-protos[grpc]==1.52.0 +googleapis-common-protos[grpc]==1.56.0 # via # google-api-core # grpc-google-iam-v1 + # grpcio-status +greenlet==1.1.2 + # via sqlalchemy grpc-google-iam-v1==0.12.3 # via google-cloud-pubsub -grpcio==1.31.0 +grpcio==1.45.0 # via # google-api-core # googleapis-common-protos # grpc-google-iam-v1 -idna==2.10 + # grpcio-status +grpcio-status==1.45.0 + # via google-api-core +idna==3.3 # via requests -imagesize==1.2.0 +imagesize==1.3.0 # via sphinx -importlib-metadata==1.7.0 +importlib-metadata==4.8.3 # via - # flake8 # jsonschema # pluggy # pytest -iniconfig==1.0.1 + # redis + # sphinx + # sqlalchemy +iniconfig==1.1.1 # via pytest -jinja2==2.11.2 +jinja2==3.0.3 # via sphinx jsonschema==3.2.0 # via docker-compose kafka-python==2.0.2 # via testcontainers -markupsafe==1.1.1 +markupsafe==2.0.1 # via jinja2 mccabe==0.6.1 # via flake8 -more-itertools==8.4.0 - # via pytest -neo4j==4.1.0 +neo4j==4.4.2 # via testcontainers -packaging==20.4 +packaging==21.3 # via # deprecation # pytest + # redis # sphinx -paramiko==2.7.1 +paramiko==2.10.3 # via docker pika==1.2.0 # via testcontainers -pluggy==0.13.1 +pluggy==1.0.0 # via pytest -protobuf==3.13.0 +protobuf==3.19.4 # via # google-api-core # googleapis-common-protos -psycopg2-binary==2.8.5 + # grpcio-status +psycopg2-binary==2.9.3 # via testcontainers -py==1.9.0 +py==1.11.0 # via pytest pyasn1==0.4.8 # via @@ -126,103 +139,104 @@ pyasn1==0.4.8 # rsa pyasn1-modules==0.2.8 # via google-auth -pycodestyle==2.6.0 +pycodestyle==2.5.0 # via flake8 -pycparser==2.20 +pycparser==2.21 # via cffi -pyflakes==2.2.0 +pyflakes==2.1.1 # via flake8 -pygments==2.6.1 +pygments==2.11.2 # via sphinx -pymongo==3.11.0 +pymongo==4.0.2 # via testcontainers -pymysql==0.10.0 +pymssql==2.2.4 # via testcontainers -pynacl==1.4.0 - # via paramiko -pyodbc==4.0.30 +pymysql==1.0.2 # via testcontainers -pyparsing==2.4.7 +pynacl==1.5.0 + # via paramiko +pyparsing==3.0.7 # via packaging -pyrsistent==0.16.0 +pyrsistent==0.18.0 # via jsonschema -pytest==6.0.1 +pytest==7.0.1 # via # -r requirements.in # pytest-cov -pytest-cov==2.10.1 +pytest-cov==3.0.0 # via -r requirements.in -python-dotenv==0.14.0 +python-dotenv==0.20.0 # via docker-compose -pytz==2020.1 +pytz==2022.1 # via # babel - # google-api-core # neo4j -pyyaml==5.3.1 +pyyaml==5.4.1 # via docker-compose -redis==3.5.3 +redis==4.2.0 # via testcontainers -requests==2.24.0 +requests==2.27.1 # via # codecov # docker # docker-compose # google-api-core # sphinx -rsa==4.6 +rsa==4.8 # via google-auth selenium==3.141.0 # via testcontainers -six==1.15.0 +six==1.16.0 # via # bcrypt - # cryptography - # docker - # docker-compose # dockerpty - # google-api-core # google-auth # grpcio # jsonschema - # packaging - # protobuf - # pynacl - # pyrsistent + # paramiko # websocket-client -snowballstemmer==2.0.0 +snowballstemmer==2.2.0 # via sphinx -sphinx==3.2.1 +sphinx==4.4.0 # via -r requirements.in sphinxcontrib-applehelp==1.0.2 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx -sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-htmlhelp==2.0.0 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.4 +sphinxcontrib-serializinghtml==1.1.5 # via sphinx -sqlalchemy==1.3.18 +sqlalchemy==1.4.32 # via testcontainers -texttable==1.6.2 +texttable==1.6.4 # via docker-compose -toml==0.10.1 - # via pytest -urllib3==1.25.10 +tomli==1.2.3 + # via + # coverage + # pytest +typing-extensions==4.1.1 + # via + # async-timeout + # importlib-metadata + # redis +urllib3==1.26.9 # via # requests # selenium -websocket-client==0.57.0 +websocket-client==0.59.0 # via # docker # docker-compose -wrapt==1.12.1 - # via testcontainers -zipp==3.1.0 +wrapt==1.14.0 + # via + # deprecated + # testcontainers +zipp==3.6.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/3.7.txt b/requirements/3.7.txt index 59383778..9a557afe 100644 --- a/requirements/3.7.txt +++ b/requirements/3.7.txt @@ -8,117 +8,148 @@ # via -r requirements.in alabaster==0.7.12 # via sphinx -attrs==19.3.0 +async-generator==1.10 + # via + # trio + # trio-websocket +async-timeout==4.0.2 + # via redis +attrs==21.4.0 # via # jsonschema + # outcome # pytest -babel==2.8.0 + # trio +babel==2.9.1 # via sphinx bcrypt==3.2.0 # via paramiko -cached-property==1.5.1 +cached-property==1.5.2 # via docker-compose -cachetools==4.1.1 +cachetools==5.0.0 # via google-auth -certifi==2020.6.20 - # via requests -cffi==1.14.2 +certifi==2021.10.8 + # via + # requests + # urllib3 +cffi==1.15.0 # via # bcrypt # cryptography # pynacl -chardet==3.0.4 +charset-normalizer==2.0.12 # via requests -codecov==2.1.8 +codecov==2.1.12 # via -r requirements.in -coverage==5.2.1 +coverage[toml]==6.3.2 # via # codecov # pytest-cov -cryptography==3.0 - # via paramiko -cx-oracle==8.0.0 +cryptography==36.0.2 + # via + # paramiko + # pyopenssl + # urllib3 +cx-oracle==8.3.0 # via testcontainers +deprecated==1.2.13 + # via redis deprecation==2.1.0 # via testcontainers -distro==1.5.0 +distro==1.7.0 # via docker-compose -docker[ssh]==4.2.2 +docker[ssh]==5.0.3 # via - # -r requirements.in # docker-compose # testcontainers -docker-compose==1.26.2 +docker-compose==1.29.2 # via testcontainers dockerpty==0.4.1 # via docker-compose docopt==0.6.2 # via docker-compose -docutils==0.16 +docutils==0.17.1 # via sphinx -flake8==3.8.3 +entrypoints==0.3 + # via flake8 +flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==1.22.1 +google-api-core[grpc]==2.7.1 # via google-cloud-pubsub -google-auth==1.20.1 +google-auth==2.6.2 # via google-api-core google-cloud-pubsub==1.7.0 # via testcontainers -googleapis-common-protos[grpc]==1.52.0 +googleapis-common-protos[grpc]==1.56.0 # via # google-api-core # grpc-google-iam-v1 + # grpcio-status +greenlet==1.1.2 + # via sqlalchemy grpc-google-iam-v1==0.12.3 # via google-cloud-pubsub -grpcio==1.31.0 +grpcio==1.45.0 # via # google-api-core # googleapis-common-protos # grpc-google-iam-v1 -idna==2.10 - # via requests -imagesize==1.2.0 + # grpcio-status +grpcio-status==1.45.0 + # via google-api-core +h11==0.13.0 + # via wsproto +idna==3.3 + # via + # requests + # trio + # urllib3 +imagesize==1.3.0 # via sphinx -importlib-metadata==1.7.0 +importlib-metadata==4.11.3 # via - # flake8 # jsonschema # pluggy # pytest -iniconfig==1.0.1 + # redis + # sphinx + # sqlalchemy +iniconfig==1.1.1 # via pytest -jinja2==2.11.2 +jinja2==3.1.1 # via sphinx jsonschema==3.2.0 # via docker-compose kafka-python==2.0.2 # via testcontainers -markupsafe==1.1.1 +markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 -more-itertools==8.4.0 - # via pytest -neo4j==4.1.0 +neo4j==4.4.2 # via testcontainers -packaging==20.4 +outcome==1.1.0 + # via trio +packaging==21.3 # via # deprecation # pytest + # redis # sphinx -paramiko==2.7.1 +paramiko==2.10.3 # via docker pika==1.2.0 # via testcontainers -pluggy==0.13.1 +pluggy==1.0.0 # via pytest -protobuf==3.13.0 +protobuf==3.19.4 # via # google-api-core # googleapis-common-protos -psycopg2-binary==2.8.5 + # grpcio-status +psycopg2-binary==2.9.3 # via testcontainers -py==1.9.0 +py==1.11.0 # via pytest pyasn1==0.4.8 # via @@ -126,103 +157,121 @@ pyasn1==0.4.8 # rsa pyasn1-modules==0.2.8 # via google-auth -pycodestyle==2.6.0 +pycodestyle==2.5.0 # via flake8 -pycparser==2.20 +pycparser==2.21 # via cffi -pyflakes==2.2.0 +pyflakes==2.1.1 # via flake8 -pygments==2.6.1 +pygments==2.11.2 # via sphinx -pymongo==3.11.0 +pymongo==4.0.2 # via testcontainers -pymysql==0.10.0 +pymssql==2.2.4 # via testcontainers -pynacl==1.4.0 - # via paramiko -pyodbc==4.0.30 +pymysql==1.0.2 # via testcontainers -pyparsing==2.4.7 +pynacl==1.5.0 + # via paramiko +pyopenssl==22.0.0 + # via urllib3 +pyparsing==3.0.7 # via packaging -pyrsistent==0.16.0 +pyrsistent==0.18.1 # via jsonschema -pytest==6.0.1 +pysocks==1.7.1 + # via urllib3 +pytest==7.1.1 # via # -r requirements.in # pytest-cov -pytest-cov==2.10.1 +pytest-cov==3.0.0 # via -r requirements.in -python-dotenv==0.14.0 +python-dotenv==0.20.0 # via docker-compose -pytz==2020.1 +pytz==2022.1 # via # babel - # google-api-core # neo4j -pyyaml==5.3.1 +pyyaml==5.4.1 # via docker-compose -redis==3.5.3 +redis==4.2.0 # via testcontainers -requests==2.24.0 +requests==2.27.1 # via # codecov # docker # docker-compose # google-api-core # sphinx -rsa==4.6 +rsa==4.8 # via google-auth -selenium==3.141.0 +selenium==4.1.3 # via testcontainers -six==1.15.0 +six==1.16.0 # via # bcrypt - # cryptography - # docker - # docker-compose # dockerpty - # google-api-core # google-auth # grpcio # jsonschema - # packaging - # protobuf - # pynacl - # pyrsistent + # paramiko # websocket-client -snowballstemmer==2.0.0 +sniffio==1.2.0 + # via trio +snowballstemmer==2.2.0 # via sphinx -sphinx==3.2.1 +sortedcontainers==2.4.0 + # via trio +sphinx==4.4.0 # via -r requirements.in sphinxcontrib-applehelp==1.0.2 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx -sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-htmlhelp==2.0.0 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.4 +sphinxcontrib-serializinghtml==1.1.5 # via sphinx -sqlalchemy==1.3.18 +sqlalchemy==1.4.32 # via testcontainers -texttable==1.6.2 +texttable==1.6.4 # via docker-compose -toml==0.10.1 - # via pytest -urllib3==1.25.10 +tomli==2.0.1 + # via + # coverage + # pytest +trio==0.20.0 + # via + # selenium + # trio-websocket +trio-websocket==0.9.2 + # via selenium +typing-extensions==4.1.1 + # via + # async-timeout + # h11 + # importlib-metadata + # redis +urllib3[secure,socks]==1.26.9 # via # requests # selenium -websocket-client==0.57.0 +websocket-client==0.59.0 # via # docker # docker-compose -wrapt==1.12.1 - # via testcontainers -zipp==3.1.0 +wrapt==1.14.0 + # via + # deprecated + # testcontainers +wsproto==1.1.0 + # via trio-websocket +zipp==3.7.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/3.8.txt b/requirements/3.8.txt index 10dfaa4d..be3d9548 100644 --- a/requirements/3.8.txt +++ b/requirements/3.8.txt @@ -8,111 +8,140 @@ # via -r requirements.in alabaster==0.7.12 # via sphinx -attrs==19.3.0 +async-generator==1.10 + # via + # trio + # trio-websocket +async-timeout==4.0.2 + # via redis +attrs==21.4.0 # via # jsonschema + # outcome # pytest -babel==2.8.0 + # trio +babel==2.9.1 # via sphinx bcrypt==3.2.0 # via paramiko -cached-property==1.5.1 - # via docker-compose -cachetools==4.1.1 +cachetools==5.0.0 # via google-auth -certifi==2020.6.20 - # via requests -cffi==1.14.2 +certifi==2021.10.8 + # via + # requests + # urllib3 +cffi==1.15.0 # via # bcrypt # cryptography # pynacl -chardet==3.0.4 +charset-normalizer==2.0.12 # via requests -codecov==2.1.8 +codecov==2.1.12 # via -r requirements.in -coverage==5.2.1 +coverage[toml]==6.3.2 # via # codecov # pytest-cov -cryptography==3.0 - # via paramiko -cx-oracle==8.0.0 +cryptography==36.0.2 + # via + # paramiko + # pyopenssl + # urllib3 +cx-oracle==8.3.0 # via testcontainers +deprecated==1.2.13 + # via redis deprecation==2.1.0 # via testcontainers -distro==1.5.0 +distro==1.7.0 # via docker-compose -docker[ssh]==4.2.2 +docker[ssh]==5.0.3 # via - # -r requirements.in # docker-compose # testcontainers -docker-compose==1.26.2 +docker-compose==1.29.2 # via testcontainers dockerpty==0.4.1 # via docker-compose docopt==0.6.2 # via docker-compose -docutils==0.16 +docutils==0.17.1 # via sphinx -flake8==3.8.3 +entrypoints==0.3 + # via flake8 +flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==1.22.1 +google-api-core[grpc]==2.7.1 # via google-cloud-pubsub -google-auth==1.20.1 +google-auth==2.6.2 # via google-api-core google-cloud-pubsub==1.7.0 # via testcontainers -googleapis-common-protos[grpc]==1.52.0 +googleapis-common-protos[grpc]==1.56.0 # via # google-api-core # grpc-google-iam-v1 + # grpcio-status +greenlet==1.1.2 + # via sqlalchemy grpc-google-iam-v1==0.12.3 # via google-cloud-pubsub -grpcio==1.31.0 +grpcio==1.45.0 # via # google-api-core # googleapis-common-protos # grpc-google-iam-v1 -idna==2.10 - # via requests -imagesize==1.2.0 + # grpcio-status +grpcio-status==1.45.0 + # via google-api-core +h11==0.13.0 + # via wsproto +idna==3.3 + # via + # requests + # trio + # urllib3 +imagesize==1.3.0 + # via sphinx +importlib-metadata==4.11.3 # via sphinx -iniconfig==1.0.1 +iniconfig==1.1.1 # via pytest -jinja2==2.11.2 +jinja2==3.1.1 # via sphinx jsonschema==3.2.0 # via docker-compose kafka-python==2.0.2 # via testcontainers -markupsafe==1.1.1 +markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 -more-itertools==8.4.0 - # via pytest -neo4j==4.1.0 +neo4j==4.4.2 # via testcontainers -packaging==20.4 +outcome==1.1.0 + # via trio +packaging==21.3 # via # deprecation # pytest + # redis # sphinx -paramiko==2.7.1 +paramiko==2.10.3 # via docker pika==1.2.0 # via testcontainers -pluggy==0.13.1 +pluggy==1.0.0 # via pytest -protobuf==3.13.0 +protobuf==3.19.4 # via # google-api-core # googleapis-common-protos -psycopg2-binary==2.8.5 + # grpcio-status +psycopg2-binary==2.9.3 # via testcontainers -py==1.9.0 +py==1.11.0 # via pytest pyasn1==0.4.8 # via @@ -120,102 +149,118 @@ pyasn1==0.4.8 # rsa pyasn1-modules==0.2.8 # via google-auth -pycodestyle==2.6.0 +pycodestyle==2.5.0 # via flake8 -pycparser==2.20 +pycparser==2.21 # via cffi -pyflakes==2.2.0 +pyflakes==2.1.1 # via flake8 -pygments==2.6.1 +pygments==2.11.2 # via sphinx -pymongo==3.11.0 +pymongo==4.0.2 # via testcontainers -pymysql==0.10.0 +pymssql==2.2.4 # via testcontainers -pynacl==1.4.0 - # via paramiko -pyodbc==4.0.30 +pymysql==1.0.2 # via testcontainers -pyparsing==2.4.7 +pynacl==1.5.0 + # via paramiko +pyopenssl==22.0.0 + # via urllib3 +pyparsing==3.0.7 # via packaging -pyrsistent==0.16.0 +pyrsistent==0.18.1 # via jsonschema -pytest==6.0.1 +pysocks==1.7.1 + # via urllib3 +pytest==7.1.1 # via # -r requirements.in # pytest-cov -pytest-cov==2.10.1 +pytest-cov==3.0.0 # via -r requirements.in -python-dotenv==0.14.0 +python-dotenv==0.20.0 # via docker-compose -pytz==2020.1 +pytz==2022.1 # via # babel - # google-api-core # neo4j -pyyaml==5.3.1 +pyyaml==5.4.1 # via docker-compose -redis==3.5.3 +redis==4.2.0 # via testcontainers -requests==2.24.0 +requests==2.27.1 # via # codecov # docker # docker-compose # google-api-core # sphinx -rsa==4.6 +rsa==4.8 # via google-auth -selenium==3.141.0 +selenium==4.1.3 # via testcontainers -six==1.15.0 +six==1.16.0 # via # bcrypt - # cryptography - # docker - # docker-compose # dockerpty - # google-api-core # google-auth # grpcio # jsonschema - # packaging - # protobuf - # pynacl - # pyrsistent + # paramiko # websocket-client -snowballstemmer==2.0.0 +sniffio==1.2.0 + # via trio +snowballstemmer==2.2.0 # via sphinx -sphinx==3.2.1 +sortedcontainers==2.4.0 + # via trio +sphinx==4.4.0 # via -r requirements.in sphinxcontrib-applehelp==1.0.2 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx -sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-htmlhelp==2.0.0 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.4 +sphinxcontrib-serializinghtml==1.1.5 # via sphinx -sqlalchemy==1.3.18 +sqlalchemy==1.4.32 # via testcontainers -texttable==1.6.2 +texttable==1.6.4 # via docker-compose -toml==0.10.1 - # via pytest -urllib3==1.25.10 +tomli==2.0.1 + # via + # coverage + # pytest +trio==0.20.0 + # via + # selenium + # trio-websocket +trio-websocket==0.9.2 + # via selenium +typing-extensions==4.1.1 + # via redis +urllib3[secure,socks]==1.26.9 # via # requests # selenium -websocket-client==0.57.0 +websocket-client==0.59.0 # via # docker # docker-compose -wrapt==1.12.1 - # via testcontainers +wrapt==1.14.0 + # via + # deprecated + # testcontainers +wsproto==1.1.0 + # via trio-websocket +zipp==3.7.0 + # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/requirements/3.9.txt b/requirements/3.9.txt new file mode 100644 index 00000000..8cb78d91 --- /dev/null +++ b/requirements/3.9.txt @@ -0,0 +1,266 @@ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# pip-compile --output-file=requirements/3.9.txt requirements.in +# +-e file:. + # via -r requirements.in +alabaster==0.7.12 + # via sphinx +async-generator==1.10 + # via + # trio + # trio-websocket +async-timeout==4.0.2 + # via redis +attrs==21.4.0 + # via + # jsonschema + # outcome + # pytest + # trio +babel==2.9.1 + # via sphinx +bcrypt==3.2.0 + # via paramiko +cachetools==5.0.0 + # via google-auth +certifi==2021.10.8 + # via + # requests + # urllib3 +cffi==1.15.0 + # via + # bcrypt + # cryptography + # pynacl +charset-normalizer==2.0.12 + # via requests +codecov==2.1.12 + # via -r requirements.in +coverage[toml]==6.3.2 + # via + # codecov + # pytest-cov +cryptography==36.0.2 + # via + # paramiko + # pyopenssl + # urllib3 +cx-oracle==8.3.0 + # via testcontainers +deprecated==1.2.13 + # via redis +deprecation==2.1.0 + # via testcontainers +distro==1.7.0 + # via docker-compose +docker[ssh]==5.0.3 + # via + # docker-compose + # testcontainers +docker-compose==1.29.2 + # via testcontainers +dockerpty==0.4.1 + # via docker-compose +docopt==0.6.2 + # via docker-compose +docutils==0.17.1 + # via sphinx +entrypoints==0.3 + # via flake8 +flake8==3.7.9 + # via -r requirements.in +google-api-core[grpc]==2.7.1 + # via google-cloud-pubsub +google-auth==2.6.2 + # via google-api-core +google-cloud-pubsub==1.7.0 + # via testcontainers +googleapis-common-protos[grpc]==1.56.0 + # via + # google-api-core + # grpc-google-iam-v1 + # grpcio-status +greenlet==1.1.2 + # via sqlalchemy +grpc-google-iam-v1==0.12.3 + # via google-cloud-pubsub +grpcio==1.45.0 + # via + # google-api-core + # googleapis-common-protos + # grpc-google-iam-v1 + # grpcio-status +grpcio-status==1.45.0 + # via google-api-core +h11==0.13.0 + # via wsproto +idna==3.3 + # via + # requests + # trio + # urllib3 +imagesize==1.3.0 + # via sphinx +importlib-metadata==4.11.3 + # via sphinx +iniconfig==1.1.1 + # via pytest +jinja2==3.1.1 + # via sphinx +jsonschema==3.2.0 + # via docker-compose +kafka-python==2.0.2 + # via testcontainers +markupsafe==2.1.1 + # via jinja2 +mccabe==0.6.1 + # via flake8 +neo4j==4.4.2 + # via testcontainers +outcome==1.1.0 + # via trio +packaging==21.3 + # via + # deprecation + # pytest + # redis + # sphinx +paramiko==2.10.3 + # via docker +pika==1.2.0 + # via testcontainers +pluggy==1.0.0 + # via pytest +protobuf==3.19.4 + # via + # google-api-core + # googleapis-common-protos + # grpcio-status +psycopg2-binary==2.9.3 + # via testcontainers +py==1.11.0 + # via pytest +pyasn1==0.4.8 + # via + # pyasn1-modules + # rsa +pyasn1-modules==0.2.8 + # via google-auth +pycodestyle==2.5.0 + # via flake8 +pycparser==2.21 + # via cffi +pyflakes==2.1.1 + # via flake8 +pygments==2.11.2 + # via sphinx +pymongo==4.0.2 + # via testcontainers +pymssql==2.2.4 + # via testcontainers +pymysql==1.0.2 + # via testcontainers +pynacl==1.5.0 + # via paramiko +pyopenssl==22.0.0 + # via urllib3 +pyparsing==3.0.7 + # via packaging +pyrsistent==0.18.1 + # via jsonschema +pysocks==1.7.1 + # via urllib3 +pytest==7.1.1 + # via + # -r requirements.in + # pytest-cov +pytest-cov==3.0.0 + # via -r requirements.in +python-dotenv==0.20.0 + # via docker-compose +pytz==2022.1 + # via + # babel + # neo4j +pyyaml==5.4.1 + # via docker-compose +redis==4.2.0 + # via testcontainers +requests==2.27.1 + # via + # codecov + # docker + # docker-compose + # google-api-core + # sphinx +rsa==4.8 + # via google-auth +selenium==4.1.3 + # via testcontainers +six==1.16.0 + # via + # bcrypt + # dockerpty + # google-auth + # grpcio + # jsonschema + # paramiko + # websocket-client +sniffio==1.2.0 + # via trio +snowballstemmer==2.2.0 + # via sphinx +sortedcontainers==2.4.0 + # via trio +sphinx==4.4.0 + # via -r requirements.in +sphinxcontrib-applehelp==1.0.2 + # via sphinx +sphinxcontrib-devhelp==1.0.2 + # via sphinx +sphinxcontrib-htmlhelp==2.0.0 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.3 + # via sphinx +sphinxcontrib-serializinghtml==1.1.5 + # via sphinx +sqlalchemy==1.4.32 + # via testcontainers +texttable==1.6.4 + # via docker-compose +tomli==2.0.1 + # via + # coverage + # pytest +trio==0.20.0 + # via + # selenium + # trio-websocket +trio-websocket==0.9.2 + # via selenium +typing-extensions==4.1.1 + # via redis +urllib3[secure,socks]==1.26.9 + # via + # requests + # selenium +websocket-client==0.59.0 + # via + # docker + # docker-compose +wrapt==1.14.0 + # via + # deprecated + # testcontainers +wsproto==1.1.0 + # via trio-websocket +zipp==3.7.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/setup.cfg b/setup.cfg index c82dc448..d5bfb97b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,16 +1,13 @@ -[bumpversion] -current_version = 2.6.0 -tag = True -commit = True - -[bumpversion:file:setup.py] - [bdist_wheel] universal = 1 [metadata] -description-file = README.md +description-file = README.rst [flake8] max-line-length = 100 exclude = .git,__pycache__,build,dist,venv,.venv + +[tools:pytest] +log_cli_level = INFO +log_cli = true diff --git a/setup.py b/setup.py index 4e38fee1..827b4d35 100644 --- a/setup.py +++ b/setup.py @@ -58,10 +58,10 @@ 'oracle': ['sqlalchemy', 'cx_Oracle'], 'postgresql': ['sqlalchemy', 'psycopg2-binary'], 'selenium': ['selenium'], - 'google-cloud-pubsub': ['google-cloud-pubsub'], + 'google-cloud-pubsub': ['google-cloud-pubsub < 2'], 'mongo': ['pymongo'], 'redis': ['redis'], - 'mssqlserver': ['pyodbc'], + 'mssqlserver': ['pymssql'], 'neo4j': ['neo4j'], 'kafka': ['kafka-python'], 'rabbitmq': ['pika'], diff --git a/testcontainers/compose.py b/testcontainers/compose.py index 20514410..89759a69 100644 --- a/testcontainers/compose.py +++ b/testcontainers/compose.py @@ -120,7 +120,7 @@ def _get_service_info(self, service, port): .format(port, service)) return result - @wait_container_is_ready() + @wait_container_is_ready(requests.exceptions.ConnectionError) def wait_for(self, url): requests.get(url) return self diff --git a/testcontainers/core/container.py b/testcontainers/core/container.py index 0ca126ca..3f4c3c04 100644 --- a/testcontainers/core/container.py +++ b/testcontainers/core/container.py @@ -3,7 +3,7 @@ from testcontainers.core.docker_client import DockerClient from testcontainers.core.exceptions import ContainerStartException -from testcontainers.core.utils import setup_logger, inside_container +from testcontainers.core.utils import setup_logger, inside_container, is_arm logger = setup_logger(__name__) @@ -42,6 +42,11 @@ def with_kwargs(self, **kwargs) -> 'DockerContainer': self._kwargs = kwargs return self + def maybe_emulate_amd64(self) -> 'DockerContainer': + if is_arm(): + return self.with_kwargs(platform='linux/amd64') + return self + def start(self): logger.info("Pulling image %s", self.image) docker_client = self.get_docker_client() diff --git a/testcontainers/core/docker_client.py b/testcontainers/core/docker_client.py index 7acc1f3e..e65d0f63 100644 --- a/testcontainers/core/docker_client.py +++ b/testcontainers/core/docker_client.py @@ -41,14 +41,24 @@ def run(self, image: str, **kwargs) def port(self, container_id, port): - return self.client.api.port(container_id, port)[0]["HostPort"] + port_mappings = self.client.api.port(container_id, port) + if not port_mappings: + raise RuntimeError(f'port mapping for container {container_id} and port {port} is not ' + 'available') + return port_mappings[0]["HostPort"] + + def get_container(self, container_id): + containers = self.client.api.containers(filters={'id': container_id}) + if not containers: + raise RuntimeError(f'could not get container with id {container_id}') + return containers[0] def bridge_ip(self, container_id): - container = self.client.api.containers(filters={'id': container_id})[0] + container = self.get_container(container_id) return container['NetworkSettings']['Networks']['bridge']['IPAddress'] def gateway_ip(self, container_id): - container = self.client.api.containers(filters={'id': container_id})[0] + container = self.get_container(container_id) return container['NetworkSettings']['Networks']['bridge']['Gateway'] def host(self): diff --git a/testcontainers/core/generic.py b/testcontainers/core/generic.py index a486801f..3fd59ae3 100644 --- a/testcontainers/core/generic.py +++ b/testcontainers/core/generic.py @@ -14,13 +14,14 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready from deprecation import deprecated +from sqlalchemy.exc import OperationalError class DbContainer(DockerContainer): def __init__(self, image, **kwargs): super(DbContainer, self).__init__(image, **kwargs) - @wait_container_is_ready() + @wait_container_is_ready(OperationalError) def _connect(self): import sqlalchemy engine = sqlalchemy.create_engine(self.get_connection_url()) diff --git a/testcontainers/core/utils.py b/testcontainers/core/utils.py index fe14d724..47fd1de2 100644 --- a/testcontainers/core/utils.py +++ b/testcontainers/core/utils.py @@ -1,4 +1,5 @@ import os +import platform import sys import subprocess import logging @@ -39,6 +40,10 @@ def is_windows(): return WIN == os_name() +def is_arm(): + return platform.machine() in ('arm64', 'aarch64') + + def inside_container(): """ Returns true if we are running inside a container. diff --git a/testcontainers/core/waiting_utils.py b/testcontainers/core/waiting_utils.py index 9b224d3b..e9953ab3 100644 --- a/testcontainers/core/waiting_utils.py +++ b/testcontainers/core/waiting_utils.py @@ -14,6 +14,7 @@ import re import time +import traceback import wrapt @@ -24,7 +25,11 @@ logger = setup_logger(__name__) -def wait_container_is_ready(): +# Get a tuple of transient exceptions for which we'll retry. Other exceptions will be raised. +TRANSIENT_EXCEPTIONS = (TimeoutError, ConnectionResetError, BrokenPipeError) + + +def wait_container_is_ready(*transient_exceptions): """ Wait until container is ready. Function that spawn container should be decorated by this method @@ -33,22 +38,23 @@ def wait_container_is_ready(): :return: """ + transient_exceptions = TRANSIENT_EXCEPTIONS + tuple(transient_exceptions) + @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): exception = None logger.info("Waiting to be ready...") - for _ in range(0, config.MAX_TRIES): + for _ in range(config.MAX_TRIES): try: return wrapped(*args, **kwargs) - except Exception as e: + except transient_exceptions as e: + logger.info('container is not yet ready: %s', traceback.format_exc()) time.sleep(config.SLEEP_TIME) exception = e raise TimeoutException( - """Wait time exceeded {0} sec. - Method {1}, args {2} , kwargs {3}. - Exception {4}""".format(config.MAX_TRIES, - wrapped.__name__, - args, kwargs, exception)) + f'Wait time ({config.MAX_TRIES * config.SLEEP_TIME}s) exceeded for {wrapped.__name__}' + f'(args: {args}, kwargs {kwargs}). Exception: {exception}' + ) return wrapper diff --git a/testcontainers/elasticsearch.py b/testcontainers/elasticsearch.py index 692b90d2..df9203b8 100644 --- a/testcontainers/elasticsearch.py +++ b/testcontainers/elasticsearch.py @@ -27,7 +27,7 @@ class ElasticSearchContainer(DockerContainer): with ElasticSearchContainer() as es: connection_url = es.get_url() """ - def __init__(self, image="elasticsearch:7.5.0", port_to_expose=9200): + def __init__(self, image="elasticsearch", port_to_expose=9200): super(ElasticSearchContainer, self).__init__(image) self.port_to_expose = port_to_expose self.with_exposed_ports(self.port_to_expose) diff --git a/testcontainers/kafka.py b/testcontainers/kafka.py index 23bf69e1..c85c28eb 100644 --- a/testcontainers/kafka.py +++ b/testcontainers/kafka.py @@ -4,7 +4,7 @@ from textwrap import dedent from kafka import KafkaConsumer -from kafka.errors import KafkaError +from kafka.errors import KafkaError, UnrecognizedBrokerVersion, NoBrokersAvailable from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready @@ -35,7 +35,7 @@ def get_bootstrap_server(self): port = self.get_exposed_port(self.port_to_expose) return '{}:{}'.format(host, port) - @wait_container_is_ready() + @wait_container_is_ready(UnrecognizedBrokerVersion, NoBrokersAvailable, KafkaError, ValueError) def _connect(self): bootstrap_server = self.get_bootstrap_server() consumer = KafkaConsumer(group_id='test', bootstrap_servers=[bootstrap_server]) diff --git a/testcontainers/mssql.py b/testcontainers/mssql.py index de96c406..7f2e4a10 100644 --- a/testcontainers/mssql.py +++ b/testcontainers/mssql.py @@ -20,35 +20,25 @@ class SqlServerContainer(DbContainer): Requires `ODBC Driver 17 for SQL Server `_. """ - SQLSERVER_PASSWORD = environ.get("SQLSERVER_PASSWORD", "1Secure*Password1") - def __init__(self, image="mcr.microsoft.com/mssql/server:2019-latest", user="SA", password=None, - port=1433, dbname="tempdb", driver="ODBC Driver 17 for SQL Server"): + port=1433, dbname="tempdb", dialect='mssql+pymssql'): super(SqlServerContainer, self).__init__(image) - self.SQLSERVER_PASSWORD = password or self.SQLSERVER_PASSWORD + self.SQLSERVER_PASSWORD = password or environ.get("SQLSERVER_PASSWORD", "1Secure*Password1") self.port_to_expose = port self.SQLSERVER_USER = user self.SQLSERVER_DBNAME = dbname - self.SQLSERVER_DRIVER = driver - - self.with_exposed_ports(self.port_to_expose) - self.ACCEPT_EULA = 'Y' - self.MSSQL_PID = 'Developer' + self.dialect = dialect def _configure(self): + self.with_exposed_ports(self.port_to_expose) self.with_env("SA_PASSWORD", self.SQLSERVER_PASSWORD) self.with_env("SQLSERVER_USER", self.SQLSERVER_USER) self.with_env("SQLSERVER_DBNAME", self.SQLSERVER_DBNAME) - self.with_env("ACCEPT_EULA", self.ACCEPT_EULA) - self.with_env("MSSQL_PID", self.MSSQL_PID) - self.with_env("SQLSERVER_DRIVER", self.SQLSERVER_DRIVER) + self.with_env("ACCEPT_EULA", 'Y') def get_connection_url(self): - standard_url = super()._create_connection_url(dialect="mssql+pyodbc", - username=self.SQLSERVER_USER, - password=self.SQLSERVER_PASSWORD, - db_name=self.SQLSERVER_DBNAME, - port=self.port_to_expose) - - return standard_url + "?driver=" + self.SQLSERVER_DRIVER + return super()._create_connection_url( + dialect=self.dialect, username=self.SQLSERVER_USER, password=self.SQLSERVER_PASSWORD, + db_name=self.SQLSERVER_DBNAME, port=self.port_to_expose + ) diff --git a/testcontainers/mysql.py b/testcontainers/mysql.py index 6f1a507c..8570589f 100644 --- a/testcontainers/mysql.py +++ b/testcontainers/mysql.py @@ -33,23 +33,15 @@ class MySqlContainer(DbContainer): result = e.execute("select version()") version, = result.fetchone() """ - MYSQL_USER = environ.get("MYSQL_USER", "test") - MYSQL_ROOT_PASSWORD = environ.get("MYSQL_ROOT_PASSWORD", "test") - MYSQL_PASSWORD = environ.get("MYSQL_PASSWORD", "test") - MYSQL_DATABASE = environ.get("MYSQL_DATABASE", "test") - def __init__(self, image="mysql:latest", **kwargs): super(MySqlContainer, self).__init__(image) self.port_to_expose = 3306 self.with_exposed_ports(self.port_to_expose) - if 'MYSQL_USER' in kwargs: - self.MYSQL_USER = kwargs['MYSQL_USER'] - if 'MYSQL_ROOT_PASSWORD' in kwargs: - self.MYSQL_ROOT_PASSWORD = kwargs['MYSQL_ROOT_PASSWORD'] - if 'MYSQL_PASSWORD' in kwargs: - self.MYSQL_PASSWORD = kwargs['MYSQL_PASSWORD'] - if 'MYSQL_DATABASE' in kwargs: - self.MYSQL_DATABASE = kwargs['MYSQL_DATABASE'] + self.MYSQL_USER = kwargs.get('MYSQL_USER', environ.get('MYSQL_USER', 'test')) + self.MYSQL_ROOT_PASSWORD = kwargs.get('MYSQL_ROOT_PASSWORD', + environ.get('MYSQL_ROOT_PASSWORD', 'test')) + self.MYSQL_PASSWORD = kwargs.get('MYSQL_PASSWORD', environ.get('MYSQL_PASSWORD', 'test')) + self.MYSQL_DATABASE = kwargs.get('MYSQL_DATABASE', environ.get('MYSQL_DATABASE', 'test')) if self.MYSQL_USER == 'root': self.MYSQL_ROOT_PASSWORD = self.MYSQL_PASSWORD diff --git a/testcontainers/rabbitmq.py b/testcontainers/rabbitmq.py index 2518204a..9c93858e 100644 --- a/testcontainers/rabbitmq.py +++ b/testcontainers/rabbitmq.py @@ -57,7 +57,7 @@ def __init__( self.with_env("RABBITMQ_DEFAULT_USER", self.RABBITMQ_DEFAULT_USER) self.with_env("RABBITMQ_DEFAULT_PASS", self.RABBITMQ_DEFAULT_PASS) - @wait_container_is_ready() + @wait_container_is_ready(pika.exceptions.IncompatibleProtocolError) def readiness_probe(self) -> bool: """Test if the RabbitMQ broker is ready.""" connection = pika.BlockingConnection(self.get_connection_params()) diff --git a/testcontainers/redis.py b/testcontainers/redis.py index 331fc546..a8007d59 100644 --- a/testcontainers/redis.py +++ b/testcontainers/redis.py @@ -11,7 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. -import redis as redis +import redis from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready @@ -23,7 +23,7 @@ def __init__(self, image="redis:latest", port_to_expose=6379): self.port_to_expose = port_to_expose self.with_exposed_ports(self.port_to_expose) - @wait_container_is_ready() + @wait_container_is_ready(redis.exceptions.ConnectionError) def _connect(self): client = self.get_client() if not client.ping(): diff --git a/testcontainers/selenium.py b/testcontainers/selenium.py index a4b3144e..e4343e90 100644 --- a/testcontainers/selenium.py +++ b/testcontainers/selenium.py @@ -19,6 +19,8 @@ from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_container_is_ready +import urllib3 + IMAGES = { "firefox": "selenium/standalone-firefox-debug:latest", @@ -59,7 +61,7 @@ def _configure(self): self.with_env("no_proxy", "localhost") self.with_env("HUB_ENV_no_proxy", "localhost") - @wait_container_is_ready() + @wait_container_is_ready(urllib3.exceptions.HTTPError) def _connect(self): from selenium import webdriver return webdriver.Remote( diff --git a/tests/test_db_containers.py b/tests/test_db_containers.py index 2fdc706e..24beb571 100644 --- a/tests/test_db_containers.py +++ b/tests/test_db_containers.py @@ -1,8 +1,9 @@ -import pytest import sqlalchemy from pymongo import MongoClient from pymongo.errors import OperationFailure +import pytest +from testcontainers.core.utils import is_arm from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for from testcontainers.mongodb import MongoDbContainer @@ -13,16 +14,17 @@ from testcontainers.postgres import PostgresContainer +@pytest.mark.skipif(is_arm(), reason='mysql container not available for ARM') def test_docker_run_mysql(): config = MySqlContainer('mysql:5.7.17') with config as mysql: e = sqlalchemy.create_engine(mysql.get_connection_url()) result = e.execute("select version()") for row in result: - assert row[0] == '5.7.17' + assert row[0].startswith('5.7.17') -def test_docker_run_postgress(): +def test_docker_run_postgres(): postgres_container = PostgresContainer("postgres:9.5") with postgres_container as postgres: e = sqlalchemy.create_engine(postgres.get_connection_url()) @@ -31,29 +33,28 @@ def test_docker_run_postgress(): print("server version:", row[0]) +@pytest.mark.skip(reason='test does not verify additional code over `test_docker_run_postgres`') def test_docker_run_greenplum(): - postgres_container = PostgresContainer("datagrip/greenplum:6.8", - user="guest", password="guest", dbname="guest") - with postgres_container as postgres: - e = sqlalchemy.create_engine(postgres.get_connection_url()) + container = PostgresContainer("datagrip/greenplum:6.8", user="guest", password="guest", + dbname="guest") + with container: + e = sqlalchemy.create_engine(container.get_connection_url()) result = e.execute("select version()") for row in result: print("server version:", row[0]) def test_docker_run_mariadb(): - mariadb_container = MySqlContainer("mariadb:10.2.9") - with mariadb_container as mariadb: + with MySqlContainer("mariadb:10.6.5").maybe_emulate_amd64() as mariadb: e = sqlalchemy.create_engine(mariadb.get_connection_url()) result = e.execute("select version()") for row in result: - assert row[0] == '10.2.9-MariaDB-10.2.9+maria~jessie' + assert row[0].startswith('10.6.5') @pytest.mark.skip(reason="needs oracle client libraries unavailable on Travis") def test_docker_run_oracle(): - oracledb_container = OracleDbContainer() - with oracledb_container as oracledb: + with OracleDbContainer() as oracledb: e = sqlalchemy.create_engine(oracledb.get_connection_url()) result = e.execute("select * from V$VERSION") versions = {'Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production', @@ -65,8 +66,7 @@ def test_docker_run_oracle(): def test_docker_run_mongodb(): - mongo_container = MongoDbContainer("mongo:latest") - with mongo_container as mongo: + with MongoDbContainer("mongo:latest") as mongo: db = mongo.get_connection_client().test doc = { "address": { @@ -94,26 +94,8 @@ def test_docker_run_mongodb_connect_without_credentials(): db.restaurants.insert_one({}) -def test_docker_run_neo4j_v35(): - neo4j_container = Neo4jContainer("neo4j:3.5") - with neo4j_container as neo4j: - with neo4j.get_driver() as driver: - with driver.session() as session: - result = session.run( - """ - CALL dbms.components() - YIELD name, versions, edition - UNWIND versions as version - RETURN name, version, edition - """) - record = result.single() - print("server version:", record["name"], record["version"], record["edition"]) - assert record["version"].startswith("3.5") - - def test_docker_run_neo4j_latest(): - neo4j_container = Neo4jContainer() - with neo4j_container as neo4j: + with Neo4jContainer() as neo4j: with neo4j.get_driver() as driver: with driver.session() as session: result = session.run( @@ -129,10 +111,7 @@ def test_docker_run_neo4j_latest(): def test_docker_generic_db(): - mongo_container = DockerContainer("mongo:latest") - mongo_container.with_bind_ports(27017, 27017) - - with mongo_container: + with DockerContainer("mongo:latest").with_bind_ports(27017, 27017) as mongo_container: def connect(): return MongoClient("mongodb://{}:{}".format(mongo_container.get_container_host_ip(), mongo_container.get_exposed_port(27017))) @@ -159,15 +138,15 @@ def connect(): def test_docker_run_mssql(): - config = SqlServerContainer() - with config as mssql: + image = 'mcr.microsoft.com/azure-sql-edge' + dialect = 'mssql+pymssql' + with SqlServerContainer(image, dialect=dialect) as mssql: e = sqlalchemy.create_engine(mssql.get_connection_url()) result = e.execute('select @@servicename') for row in result: assert row[0] == 'MSSQLSERVER' - config = SqlServerContainer(password="1Secure*Password2") - with config as mssql: + with SqlServerContainer(image, password="1Secure*Password2", dialect=dialect) as mssql: e = sqlalchemy.create_engine(mssql.get_connection_url()) result = e.execute('select @@servicename') for row in result: diff --git a/tests/test_elasticsearch.py b/tests/test_elasticsearch.py index d4734ad6..f9f769cc 100644 --- a/tests/test_elasticsearch.py +++ b/tests/test_elasticsearch.py @@ -5,6 +5,7 @@ def test_docker_run_elasticsearch(): - with ElasticSearchContainer() as es: + version = '7.16.1' + with ElasticSearchContainer(f'elasticsearch:{version}') as es: resp = urllib.request.urlopen(es.get_url()) - assert json.loads(resp.read().decode())['version']['number'] == '7.5.0' + assert json.loads(resp.read().decode())['version']['number'] == version diff --git a/tests/test_kafka.py b/tests/test_kafka.py index 3f6a80df..4efda437 100644 --- a/tests/test_kafka.py +++ b/tests/test_kafka.py @@ -1,5 +1,4 @@ from kafka import KafkaConsumer, KafkaProducer, TopicPartition - from testcontainers.kafka import KafkaContainer diff --git a/tests/test_localstack.py b/tests/test_localstack.py index d9ae3b29..8650a5a3 100644 --- a/tests/test_localstack.py +++ b/tests/test_localstack.py @@ -5,8 +5,7 @@ def test_docker_run_localstack(): - config = LocalStackContainer() - with config as localstack: + with LocalStackContainer() as localstack: resp = urllib.request.urlopen('{}/health'.format(localstack.get_url())) services = json.loads(resp.read().decode())['services'] diff --git a/tests/test_new_docker_api.py b/tests/test_new_docker_api.py index c1d4677a..644840b0 100644 --- a/tests/test_new_docker_api.py +++ b/tests/test_new_docker_api.py @@ -3,9 +3,7 @@ from pathlib import Path from testcontainers import mysql - from testcontainers.core.container import DockerContainer -from importlib import reload def setup_module(m): @@ -24,12 +22,10 @@ def test_docker_custom_image(): def test_docker_env_variables(): - reload(mysql) - - db = mysql.MySqlContainer() - db.with_bind_ports(3306, 32785) - with db: - url = db.get_connection_url() + container = mysql.MySqlContainer("mariadb:10.6.5")\ + .with_bind_ports(3306, 32785).maybe_emulate_amd64() + with container: + url = container.get_connection_url() pattern = r'mysql\+pymysql:\/\/demo:test@[\w,.]+:(3306|32785)\/custom_db' assert re.match(pattern, url) diff --git a/tests/test_webdriver_container.py b/tests/test_webdriver_container.py index a23c7ce4..e7282aa1 100644 --- a/tests/test_webdriver_container.py +++ b/tests/test_webdriver_container.py @@ -1,16 +1,15 @@ -from time import sleep - import pytest from selenium.webdriver import DesiredCapabilities from testcontainers.selenium import BrowserWebDriverContainer +from testcontainers.core.utils import is_arm @pytest.mark.parametrize("caps", [DesiredCapabilities.CHROME, DesiredCapabilities.FIREFOX]) def test_webdriver_container_container(caps): - chrome = BrowserWebDriverContainer(caps) + if is_arm(): + pytest.skip('https://github.com/SeleniumHQ/docker-selenium/issues/1076') - with chrome: + with BrowserWebDriverContainer(caps).maybe_emulate_amd64() as chrome: webdriver = chrome.get_driver() webdriver.get("http://google.com") webdriver.find_element_by_name("q").send_keys("Hello") - sleep(1)