Skip to content

Commit 2401474

Browse files
A support of Python v3.8 (#238)
* A support of Python 3.8 [typing] Python 3.8 does not support tuple[...], list[...], set[...] and so on. We will use the analogues from typing package: typing.Tuple[...], typing.List[...] and typing.Set[...]. * [CI] Jobs for testing with python 3.8.0, 3.8.latest, 3.9.latest, 3.10.latest and 3.11.latest [std2-all][alpine] are added
1 parent da2c493 commit 2401474

9 files changed

+199
-22
lines changed

.travis.yml

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ notifications:
1919
on_failure: always
2020

2121
env:
22+
- TEST_PLATFORM=std2-all PYTHON_VERSION=3.8.0 PG_VERSION=17
23+
- TEST_PLATFORM=std2-all PYTHON_VERSION=3.8 PG_VERSION=17
24+
- TEST_PLATFORM=std2-all PYTHON_VERSION=3.9 PG_VERSION=17
25+
- TEST_PLATFORM=std2-all PYTHON_VERSION=3.10 PG_VERSION=17
26+
- TEST_PLATFORM=std2-all PYTHON_VERSION=3.11 PG_VERSION=17
2227
- TEST_PLATFORM=std PYTHON_VERSION=3 PG_VERSION=16
2328
- TEST_PLATFORM=std PYTHON_VERSION=3 PG_VERSION=15
2429
- TEST_PLATFORM=std PYTHON_VERSION=3 PG_VERSION=14

Dockerfile--std2-all.tmpl

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
ARG PG_VERSION
2+
ARG PYTHON_VERSION
3+
4+
# --------------------------------------------- base1
5+
FROM postgres:${PG_VERSION}-alpine as base1
6+
7+
# --------------------------------------------- base2_with_python-3
8+
FROM base1 as base2_with_python-3
9+
RUN apk add --no-cache curl python3 python3-dev build-base musl-dev linux-headers
10+
11+
# For pyenv
12+
RUN apk add patch
13+
RUN apk add git
14+
RUN apk add xz-dev
15+
RUN apk add zip
16+
RUN apk add zlib-dev
17+
RUN apk add libffi-dev
18+
RUN apk add readline-dev
19+
RUN apk add openssl openssl-dev
20+
RUN apk add sqlite-dev
21+
RUN apk add bzip2-dev
22+
23+
# --------------------------------------------- base3_with_python-3.8.0
24+
FROM base2_with_python-3 as base3_with_python-3.8.0
25+
ENV PYTHON_VERSION=3.8.0
26+
27+
# --------------------------------------------- base3_with_python-3.8
28+
FROM base2_with_python-3 as base3_with_python-3.8
29+
ENV PYTHON_VERSION=3.8
30+
31+
# --------------------------------------------- base3_with_python-3.9
32+
FROM base2_with_python-3 as base3_with_python-3.9
33+
ENV PYTHON_VERSION=3.9
34+
35+
# --------------------------------------------- base3_with_python-3.10
36+
FROM base2_with_python-3 as base3_with_python-3.10
37+
ENV PYTHON_VERSION=3.10
38+
39+
# --------------------------------------------- base3_with_python-3.11
40+
FROM base2_with_python-3 as base3_with_python-3.11
41+
ENV PYTHON_VERSION=3.11
42+
43+
# --------------------------------------------- final
44+
FROM base3_with_python-${PYTHON_VERSION} as final
45+
46+
#RUN apk add --no-cache mc
47+
48+
# Full version of "ps" command
49+
RUN apk add --no-cache procps
50+
51+
RUN apk add --no-cache openssh
52+
RUN apk add --no-cache sudo
53+
54+
ENV LANG=C.UTF-8
55+
56+
RUN addgroup -S sudo
57+
RUN adduser postgres sudo
58+
59+
EXPOSE 22
60+
RUN ssh-keygen -A
61+
62+
ADD . /pg/testgres
63+
WORKDIR /pg/testgres
64+
RUN chown -R postgres:postgres /pg
65+
66+
# It allows to use sudo without password
67+
RUN sh -c "echo \"postgres ALL=(ALL:ALL) NOPASSWD:ALL\"">>/etc/sudoers
68+
69+
# THIS CMD IS NEEDED TO CONNECT THROUGH SSH WITHOUT PASSWORD
70+
RUN sh -c "echo "postgres:*" | chpasswd -e"
71+
72+
USER postgres
73+
74+
RUN curl https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
75+
76+
RUN ~/.pyenv/bin/pyenv install ${PYTHON_VERSION}
77+
78+
# THIS CMD IS NEEDED TO CONNECT THROUGH SSH WITHOUT PASSWORD
79+
RUN chmod 700 ~/
80+
81+
RUN mkdir -p ~/.ssh
82+
#RUN chmod 700 ~/.ssh
83+
84+
ENTRYPOINT sh -c " \
85+
set -eux; \
86+
echo HELLO FROM ENTRYPOINT; \
87+
echo HOME DIR IS [`realpath ~/`]; \
88+
ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -N ''; \
89+
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys; \
90+
chmod 600 ~/.ssh/authorized_keys; \
91+
ls -la ~/.ssh/; \
92+
sudo /usr/sbin/sshd; \
93+
ssh-keyscan -H localhost >> ~/.ssh/known_hosts; \
94+
ssh-keyscan -H 127.0.0.1 >> ~/.ssh/known_hosts; \
95+
export PATH=\"~/.pyenv/bin:$PATH\"; \
96+
TEST_FILTER=\"\" bash run_tests2.sh;"

run_tests2.sh

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2017-2025 Postgres Professional
4+
5+
set -eux
6+
7+
eval "$(pyenv init -)"
8+
eval "$(pyenv virtualenv-init -)"
9+
10+
pyenv virtualenv --force ${PYTHON_VERSION} cur
11+
pyenv activate cur
12+
13+
if [ -z ${TEST_FILTER+x} ]; \
14+
then export TEST_FILTER="TestTestgresLocal or (TestTestgresCommon and (not remote))"; \
15+
fi
16+
17+
# fail early
18+
echo check that pg_config is in PATH
19+
command -v pg_config
20+
21+
# prepare python environment
22+
VENV_PATH="/tmp/testgres_venv"
23+
rm -rf $VENV_PATH
24+
python -m venv "${VENV_PATH}"
25+
export VIRTUAL_ENV_DISABLE_PROMPT=1
26+
source "${VENV_PATH}/bin/activate"
27+
pip install coverage flake8 psutil Sphinx pytest pytest-xdist psycopg2 six psutil
28+
29+
# install testgres' dependencies
30+
export PYTHONPATH=$(pwd)
31+
# $PIP install .
32+
33+
# test code quality
34+
flake8 .
35+
36+
37+
# remove existing coverage file
38+
export COVERAGE_FILE=.coverage
39+
rm -f $COVERAGE_FILE
40+
41+
42+
# run tests (PATH)
43+
time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}"
44+
45+
46+
# run tests (PG_BIN)
47+
PG_BIN=$(pg_config --bindir) \
48+
time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}"
49+
50+
51+
# run tests (PG_CONFIG)
52+
PG_CONFIG=$(pg_config --bindir)/pg_config \
53+
time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}"
54+
55+
56+
# show coverage
57+
coverage report
58+
59+
# build documentation
60+
cd docs
61+
make html
62+
cd ..
63+
64+
# attempt to fix codecov
65+
set +eux
66+
67+
# send coverage stats to Codecov
68+
bash <(curl -s https://codecov.io/bash)

testgres/operations/remote_ops.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tempfile
66
import io
77
import logging
8+
import typing
89

910
from ..exceptions import ExecUtilException
1011
from ..exceptions import InvalidOperationException
@@ -669,8 +670,8 @@ def _is_port_free__process_1(error: str) -> bool:
669670
return True
670671

671672
@staticmethod
672-
def _make_exec_env_list() -> list[str]:
673-
result = list[str]()
673+
def _make_exec_env_list() -> typing.List[str]:
674+
result: typing.List[str] = list()
674675
for envvar in os.environ.items():
675676
if not __class__._does_put_envvar_into_exec_cmd(envvar[0]):
676677
continue

testgres/port_manager.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import threading
88
import random
9+
import typing
910

1011

1112
class PortManager:
@@ -50,16 +51,16 @@ class PortManager__Generic(PortManager):
5051
_os_ops: OsOperations
5152
_guard: object
5253
# TODO: is there better to use bitmap fot _available_ports?
53-
_available_ports: set[int]
54-
_reserved_ports: set[int]
54+
_available_ports: typing.Set[int]
55+
_reserved_ports: typing.Set[int]
5556

5657
def __init__(self, os_ops: OsOperations):
5758
assert os_ops is not None
5859
assert isinstance(os_ops, OsOperations)
5960
self._os_ops = os_ops
6061
self._guard = threading.Lock()
61-
self._available_ports = set[int](range(1024, 65535))
62-
self._reserved_ports = set[int]()
62+
self._available_ports: typing.Set[int] = set(range(1024, 65535))
63+
self._reserved_ports: typing.Set[int] = set()
6364

6465
def reserve_port(self) -> int:
6566
assert self._guard is not None

tests/conftest.py

+13-9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ class TestConfigPropNames:
2626
TEST_CFG__LOG_DIR = "TEST_CFG__LOG_DIR"
2727

2828

29+
# /////////////////////////////////////////////////////////////////////////////
30+
31+
T_TUPLE__str_int = typing.Tuple[str, int]
32+
2933
# /////////////////////////////////////////////////////////////////////////////
3034
# TestStartupData__Helper
3135

@@ -110,11 +114,11 @@ class TEST_PROCESS_STATS:
110114
cUnexpectedTests: int = 0
111115
cAchtungTests: int = 0
112116

113-
FailedTests = list[str, int]()
114-
XFailedTests = list[str, int]()
115-
NotXFailedTests = list[str]()
116-
WarningTests = list[str, int]()
117-
AchtungTests = list[str]()
117+
FailedTests: typing.List[T_TUPLE__str_int] = list()
118+
XFailedTests: typing.List[T_TUPLE__str_int] = list()
119+
NotXFailedTests: typing.List[str] = list()
120+
WarningTests: typing.List[T_TUPLE__str_int] = list()
121+
AchtungTests: typing.List[str] = list()
118122

119123
cTotalDuration: datetime.timedelta = datetime.timedelta()
120124

@@ -769,7 +773,7 @@ def helper__calc_W(n: int) -> int:
769773

770774

771775
# ------------------------------------------------------------------------
772-
def helper__print_test_list(tests: list[str]) -> None:
776+
def helper__print_test_list(tests: typing.List[str]) -> None:
773777
assert type(tests) == list # noqa: E721
774778

775779
assert helper__calc_W(9) == 1
@@ -796,7 +800,7 @@ def helper__print_test_list(tests: list[str]) -> None:
796800

797801

798802
# ------------------------------------------------------------------------
799-
def helper__print_test_list2(tests: list[str, int]) -> None:
803+
def helper__print_test_list2(tests: typing.List[T_TUPLE__str_int]) -> None:
800804
assert type(tests) == list # noqa: E721
801805

802806
assert helper__calc_W(9) == 1
@@ -843,7 +847,7 @@ def LOCAL__print_line1_with_header(header: str):
843847
assert header != ""
844848
logging.info(C_LINE1 + " [" + header + "]")
845849

846-
def LOCAL__print_test_list(header: str, test_count: int, test_list: list[str]):
850+
def LOCAL__print_test_list(header: str, test_count: int, test_list: typing.List[str]):
847851
assert type(header) == str # noqa: E721
848852
assert type(test_count) == int # noqa: E721
849853
assert type(test_list) == list # noqa: E721
@@ -858,7 +862,7 @@ def LOCAL__print_test_list(header: str, test_count: int, test_list: list[str]):
858862
logging.info("")
859863

860864
def LOCAL__print_test_list2(
861-
header: str, test_count: int, test_list: list[str, int]
865+
header: str, test_count: int, test_list: typing.List[T_TUPLE__str_int]
862866
):
863867
assert type(header) == str # noqa: E721
864868
assert type(test_count) == int # noqa: E721

tests/test_os_ops_common.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
import logging
1313
import socket
1414
import threading
15+
import typing
1516

1617
from ..testgres import InvalidOperationException
1718
from ..testgres import ExecUtilException
1819

1920

2021
class TestOsOpsCommon:
21-
sm_os_ops_descrs: list[OsOpsDescr] = [
22+
sm_os_ops_descrs: typing.List[OsOpsDescr] = [
2223
OsOpsDescrs.sm_local_os_ops_descr,
2324
OsOpsDescrs.sm_remote_os_ops_descr
2425
]

tests/test_testgres_common.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def removing(os_ops: OsOperations, f):
5656

5757

5858
class TestTestgresCommon:
59-
sm_node_svcs: list[PostgresNodeService] = [
59+
sm_node_svcs: typing.List[PostgresNodeService] = [
6060
PostgresNodeServices.sm_local,
6161
PostgresNodeServices.sm_local2,
6262
PostgresNodeServices.sm_remote,
@@ -315,8 +315,8 @@ def test_child_pids(self, node_svc: PostgresNodeService):
315315

316316
def LOCAL__test_auxiliary_pids(
317317
node: PostgresNode,
318-
expectedTypes: list[ProcessType]
319-
) -> list[ProcessType]:
318+
expectedTypes: typing.List[ProcessType]
319+
) -> typing.List[ProcessType]:
320320
# returns list of the absence processes
321321
assert node is not None
322322
assert type(node) == PostgresNode # noqa: E721
@@ -327,15 +327,15 @@ def LOCAL__test_auxiliary_pids(
327327
assert pids is not None # noqa: E721
328328
assert type(pids) == dict # noqa: E721
329329

330-
result = list[ProcessType]()
330+
result: typing.List[ProcessType] = list()
331331
for ptype in expectedTypes:
332332
if not (ptype in pids):
333333
result.append(ptype)
334334
return result
335335

336336
def LOCAL__check_auxiliary_pids__multiple_attempts(
337337
node: PostgresNode,
338-
expectedTypes: list[ProcessType]):
338+
expectedTypes: typing.List[ProcessType]):
339339
assert node is not None
340340
assert type(node) == PostgresNode # noqa: E721
341341
assert expectedTypes is not None

tests/test_utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
from ..testgres import scoped_config
88

99
import pytest
10+
import typing
1011

1112

1213
class TestUtils:
13-
sm_os_ops_descrs: list[OsOpsDescr] = [
14+
sm_os_ops_descrs: typing.List[OsOpsDescr] = [
1415
OsOpsDescrs.sm_local_os_ops_descr,
1516
OsOpsDescrs.sm_remote_os_ops_descr
1617
]

0 commit comments

Comments
 (0)